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         return v;
17788     },
17789
17790     // private
17791     formatDate : function(date, fmt){
17792         return (!date || !(date instanceof Date)) ?
17793                date : date.dateFormat(fmt || this.format);
17794     },
17795
17796     // private
17797     menuListeners : {
17798         select: function(m, d){
17799             
17800             this.setValue(d);
17801             this.fireEvent('select', this, d);
17802         },
17803         show : function(){ // retain focus styling
17804             this.onFocus();
17805         },
17806         hide : function(){
17807             this.focus.defer(10, this);
17808             var ml = this.menuListeners;
17809             this.menu.un("select", ml.select,  this);
17810             this.menu.un("show", ml.show,  this);
17811             this.menu.un("hide", ml.hide,  this);
17812         }
17813     },
17814
17815     // private
17816     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17817     onTriggerClick : function(){
17818         if(this.disabled){
17819             return;
17820         }
17821         if(this.menu == null){
17822             this.menu = new Roo.menu.DateMenu();
17823         }
17824         Roo.apply(this.menu.picker,  {
17825             showClear: this.allowBlank,
17826             minDate : this.minValue,
17827             maxDate : this.maxValue,
17828             disabledDatesRE : this.ddMatch,
17829             disabledDatesText : this.disabledDatesText,
17830             disabledDays : this.disabledDays,
17831             disabledDaysText : this.disabledDaysText,
17832             format : this.useIso ? 'Y-m-d' : this.format,
17833             minText : String.format(this.minText, this.formatDate(this.minValue)),
17834             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17835         });
17836         this.menu.on(Roo.apply({}, this.menuListeners, {
17837             scope:this
17838         }));
17839         this.menu.picker.setValue(this.getValue() || new Date());
17840         this.menu.show(this.el, "tl-bl?");
17841     },
17842
17843     beforeBlur : function(){
17844         var v = this.parseDate(this.getRawValue());
17845         if(v){
17846             this.setValue(v);
17847         }
17848     },
17849
17850     /*@
17851      * overide
17852      * 
17853      */
17854     isDirty : function() {
17855         if(this.disabled) {
17856             return false;
17857         }
17858         
17859         if(typeof(this.startValue) === 'undefined'){
17860             return false;
17861         }
17862         
17863         return String(this.getValue()) !== String(this.startValue);
17864         
17865     },
17866     // @overide
17867     cleanLeadingSpace : function(e)
17868     {
17869        return;
17870     }
17871     
17872 });/*
17873  * Based on:
17874  * Ext JS Library 1.1.1
17875  * Copyright(c) 2006-2007, Ext JS, LLC.
17876  *
17877  * Originally Released Under LGPL - original licence link has changed is not relivant.
17878  *
17879  * Fork - LGPL
17880  * <script type="text/javascript">
17881  */
17882  
17883 /**
17884  * @class Roo.form.MonthField
17885  * @extends Roo.form.TriggerField
17886  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17887 * @constructor
17888 * Create a new MonthField
17889 * @param {Object} config
17890  */
17891 Roo.form.MonthField = function(config){
17892     
17893     Roo.form.MonthField.superclass.constructor.call(this, config);
17894     
17895       this.addEvents({
17896          
17897         /**
17898          * @event select
17899          * Fires when a date is selected
17900              * @param {Roo.form.MonthFieeld} combo This combo box
17901              * @param {Date} date The date selected
17902              */
17903         'select' : true
17904          
17905     });
17906     
17907     
17908     if(typeof this.minValue == "string") {
17909         this.minValue = this.parseDate(this.minValue);
17910     }
17911     if(typeof this.maxValue == "string") {
17912         this.maxValue = this.parseDate(this.maxValue);
17913     }
17914     this.ddMatch = null;
17915     if(this.disabledDates){
17916         var dd = this.disabledDates;
17917         var re = "(?:";
17918         for(var i = 0; i < dd.length; i++){
17919             re += dd[i];
17920             if(i != dd.length-1) {
17921                 re += "|";
17922             }
17923         }
17924         this.ddMatch = new RegExp(re + ")");
17925     }
17926 };
17927
17928 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17929     /**
17930      * @cfg {String} format
17931      * The default date format string which can be overriden for localization support.  The format must be
17932      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17933      */
17934     format : "M Y",
17935     /**
17936      * @cfg {String} altFormats
17937      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17938      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17939      */
17940     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17941     /**
17942      * @cfg {Array} disabledDays
17943      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17944      */
17945     disabledDays : [0,1,2,3,4,5,6],
17946     /**
17947      * @cfg {String} disabledDaysText
17948      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17949      */
17950     disabledDaysText : "Disabled",
17951     /**
17952      * @cfg {Array} disabledDates
17953      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17954      * expression so they are very powerful. Some examples:
17955      * <ul>
17956      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17957      * <li>["03/08", "09/16"] would disable those days for every year</li>
17958      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17959      * <li>["03/../2006"] would disable every day in March 2006</li>
17960      * <li>["^03"] would disable every day in every March</li>
17961      * </ul>
17962      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17963      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17964      */
17965     disabledDates : null,
17966     /**
17967      * @cfg {String} disabledDatesText
17968      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17969      */
17970     disabledDatesText : "Disabled",
17971     /**
17972      * @cfg {Date/String} minValue
17973      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17974      * valid format (defaults to null).
17975      */
17976     minValue : null,
17977     /**
17978      * @cfg {Date/String} maxValue
17979      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17980      * valid format (defaults to null).
17981      */
17982     maxValue : null,
17983     /**
17984      * @cfg {String} minText
17985      * The error text to display when the date in the cell is before minValue (defaults to
17986      * 'The date in this field must be after {minValue}').
17987      */
17988     minText : "The date in this field must be equal to or after {0}",
17989     /**
17990      * @cfg {String} maxTextf
17991      * The error text to display when the date in the cell is after maxValue (defaults to
17992      * 'The date in this field must be before {maxValue}').
17993      */
17994     maxText : "The date in this field must be equal to or before {0}",
17995     /**
17996      * @cfg {String} invalidText
17997      * The error text to display when the date in the field is invalid (defaults to
17998      * '{value} is not a valid date - it must be in the format {format}').
17999      */
18000     invalidText : "{0} is not a valid date - it must be in the format {1}",
18001     /**
18002      * @cfg {String} triggerClass
18003      * An additional CSS class used to style the trigger button.  The trigger will always get the
18004      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18005      * which displays a calendar icon).
18006      */
18007     triggerClass : 'x-form-date-trigger',
18008     
18009
18010     /**
18011      * @cfg {Boolean} useIso
18012      * if enabled, then the date field will use a hidden field to store the 
18013      * real value as iso formated date. default (true)
18014      */ 
18015     useIso : true,
18016     /**
18017      * @cfg {String/Object} autoCreate
18018      * A DomHelper element spec, or true for a default element spec (defaults to
18019      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18020      */ 
18021     // private
18022     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18023     
18024     // private
18025     hiddenField: false,
18026     
18027     hideMonthPicker : false,
18028     
18029     onRender : function(ct, position)
18030     {
18031         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18032         if (this.useIso) {
18033             this.el.dom.removeAttribute('name'); 
18034             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18035                     'before', true);
18036             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18037             // prevent input submission
18038             this.hiddenName = this.name;
18039         }
18040             
18041             
18042     },
18043     
18044     // private
18045     validateValue : function(value)
18046     {
18047         value = this.formatDate(value);
18048         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18049             return false;
18050         }
18051         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18052              return true;
18053         }
18054         var svalue = value;
18055         value = this.parseDate(value);
18056         if(!value){
18057             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18058             return false;
18059         }
18060         var time = value.getTime();
18061         if(this.minValue && time < this.minValue.getTime()){
18062             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18063             return false;
18064         }
18065         if(this.maxValue && time > this.maxValue.getTime()){
18066             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18067             return false;
18068         }
18069         /*if(this.disabledDays){
18070             var day = value.getDay();
18071             for(var i = 0; i < this.disabledDays.length; i++) {
18072                 if(day === this.disabledDays[i]){
18073                     this.markInvalid(this.disabledDaysText);
18074                     return false;
18075                 }
18076             }
18077         }
18078         */
18079         var fvalue = this.formatDate(value);
18080         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18081             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18082             return false;
18083         }
18084         */
18085         return true;
18086     },
18087
18088     // private
18089     // Provides logic to override the default TriggerField.validateBlur which just returns true
18090     validateBlur : function(){
18091         return !this.menu || !this.menu.isVisible();
18092     },
18093
18094     /**
18095      * Returns the current date value of the date field.
18096      * @return {Date} The date value
18097      */
18098     getValue : function(){
18099         
18100         
18101         
18102         return  this.hiddenField ?
18103                 this.hiddenField.value :
18104                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18105     },
18106
18107     /**
18108      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18109      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18110      * (the default format used is "m/d/y").
18111      * <br />Usage:
18112      * <pre><code>
18113 //All of these calls set the same date value (May 4, 2006)
18114
18115 //Pass a date object:
18116 var dt = new Date('5/4/06');
18117 monthField.setValue(dt);
18118
18119 //Pass a date string (default format):
18120 monthField.setValue('5/4/06');
18121
18122 //Pass a date string (custom format):
18123 monthField.format = 'Y-m-d';
18124 monthField.setValue('2006-5-4');
18125 </code></pre>
18126      * @param {String/Date} date The date or valid date string
18127      */
18128     setValue : function(date){
18129         Roo.log('month setValue' + date);
18130         // can only be first of month..
18131         
18132         var val = this.parseDate(date);
18133         
18134         if (this.hiddenField) {
18135             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18136         }
18137         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18138         this.value = this.parseDate(date);
18139     },
18140
18141     // private
18142     parseDate : function(value){
18143         if(!value || value instanceof Date){
18144             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18145             return value;
18146         }
18147         var v = Date.parseDate(value, this.format);
18148         if (!v && this.useIso) {
18149             v = Date.parseDate(value, 'Y-m-d');
18150         }
18151         if (v) {
18152             // 
18153             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18154         }
18155         
18156         
18157         if(!v && this.altFormats){
18158             if(!this.altFormatsArray){
18159                 this.altFormatsArray = this.altFormats.split("|");
18160             }
18161             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18162                 v = Date.parseDate(value, this.altFormatsArray[i]);
18163             }
18164         }
18165         return v;
18166     },
18167
18168     // private
18169     formatDate : function(date, fmt){
18170         return (!date || !(date instanceof Date)) ?
18171                date : date.dateFormat(fmt || this.format);
18172     },
18173
18174     // private
18175     menuListeners : {
18176         select: function(m, d){
18177             this.setValue(d);
18178             this.fireEvent('select', this, d);
18179         },
18180         show : function(){ // retain focus styling
18181             this.onFocus();
18182         },
18183         hide : function(){
18184             this.focus.defer(10, this);
18185             var ml = this.menuListeners;
18186             this.menu.un("select", ml.select,  this);
18187             this.menu.un("show", ml.show,  this);
18188             this.menu.un("hide", ml.hide,  this);
18189         }
18190     },
18191     // private
18192     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18193     onTriggerClick : function(){
18194         if(this.disabled){
18195             return;
18196         }
18197         if(this.menu == null){
18198             this.menu = new Roo.menu.DateMenu();
18199            
18200         }
18201         
18202         Roo.apply(this.menu.picker,  {
18203             
18204             showClear: this.allowBlank,
18205             minDate : this.minValue,
18206             maxDate : this.maxValue,
18207             disabledDatesRE : this.ddMatch,
18208             disabledDatesText : this.disabledDatesText,
18209             
18210             format : this.useIso ? 'Y-m-d' : this.format,
18211             minText : String.format(this.minText, this.formatDate(this.minValue)),
18212             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18213             
18214         });
18215          this.menu.on(Roo.apply({}, this.menuListeners, {
18216             scope:this
18217         }));
18218        
18219         
18220         var m = this.menu;
18221         var p = m.picker;
18222         
18223         // hide month picker get's called when we called by 'before hide';
18224         
18225         var ignorehide = true;
18226         p.hideMonthPicker  = function(disableAnim){
18227             if (ignorehide) {
18228                 return;
18229             }
18230              if(this.monthPicker){
18231                 Roo.log("hideMonthPicker called");
18232                 if(disableAnim === true){
18233                     this.monthPicker.hide();
18234                 }else{
18235                     this.monthPicker.slideOut('t', {duration:.2});
18236                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18237                     p.fireEvent("select", this, this.value);
18238                     m.hide();
18239                 }
18240             }
18241         }
18242         
18243         Roo.log('picker set value');
18244         Roo.log(this.getValue());
18245         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18246         m.show(this.el, 'tl-bl?');
18247         ignorehide  = false;
18248         // this will trigger hideMonthPicker..
18249         
18250         
18251         // hidden the day picker
18252         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18253         
18254         
18255         
18256       
18257         
18258         p.showMonthPicker.defer(100, p);
18259     
18260         
18261        
18262     },
18263
18264     beforeBlur : function(){
18265         var v = this.parseDate(this.getRawValue());
18266         if(v){
18267             this.setValue(v);
18268         }
18269     }
18270
18271     /** @cfg {Boolean} grow @hide */
18272     /** @cfg {Number} growMin @hide */
18273     /** @cfg {Number} growMax @hide */
18274     /**
18275      * @hide
18276      * @method autoSize
18277      */
18278 });/*
18279  * Based on:
18280  * Ext JS Library 1.1.1
18281  * Copyright(c) 2006-2007, Ext JS, LLC.
18282  *
18283  * Originally Released Under LGPL - original licence link has changed is not relivant.
18284  *
18285  * Fork - LGPL
18286  * <script type="text/javascript">
18287  */
18288  
18289
18290 /**
18291  * @class Roo.form.ComboBox
18292  * @extends Roo.form.TriggerField
18293  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18294  * @constructor
18295  * Create a new ComboBox.
18296  * @param {Object} config Configuration options
18297  */
18298 Roo.form.ComboBox = function(config){
18299     Roo.form.ComboBox.superclass.constructor.call(this, config);
18300     this.addEvents({
18301         /**
18302          * @event expand
18303          * Fires when the dropdown list is expanded
18304              * @param {Roo.form.ComboBox} combo This combo box
18305              */
18306         'expand' : true,
18307         /**
18308          * @event collapse
18309          * Fires when the dropdown list is collapsed
18310              * @param {Roo.form.ComboBox} combo This combo box
18311              */
18312         'collapse' : true,
18313         /**
18314          * @event beforeselect
18315          * Fires before a list item is selected. Return false to cancel the selection.
18316              * @param {Roo.form.ComboBox} combo This combo box
18317              * @param {Roo.data.Record} record The data record returned from the underlying store
18318              * @param {Number} index The index of the selected item in the dropdown list
18319              */
18320         'beforeselect' : true,
18321         /**
18322          * @event select
18323          * Fires when a list item is selected
18324              * @param {Roo.form.ComboBox} combo This combo box
18325              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18326              * @param {Number} index The index of the selected item in the dropdown list
18327              */
18328         'select' : true,
18329         /**
18330          * @event beforequery
18331          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18332          * The event object passed has these properties:
18333              * @param {Roo.form.ComboBox} combo This combo box
18334              * @param {String} query The query
18335              * @param {Boolean} forceAll true to force "all" query
18336              * @param {Boolean} cancel true to cancel the query
18337              * @param {Object} e The query event object
18338              */
18339         'beforequery': true,
18340          /**
18341          * @event add
18342          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18343              * @param {Roo.form.ComboBox} combo This combo box
18344              */
18345         'add' : true,
18346         /**
18347          * @event edit
18348          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18349              * @param {Roo.form.ComboBox} combo This combo box
18350              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18351              */
18352         'edit' : true
18353         
18354         
18355     });
18356     if(this.transform){
18357         this.allowDomMove = false;
18358         var s = Roo.getDom(this.transform);
18359         if(!this.hiddenName){
18360             this.hiddenName = s.name;
18361         }
18362         if(!this.store){
18363             this.mode = 'local';
18364             var d = [], opts = s.options;
18365             for(var i = 0, len = opts.length;i < len; i++){
18366                 var o = opts[i];
18367                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18368                 if(o.selected) {
18369                     this.value = value;
18370                 }
18371                 d.push([value, o.text]);
18372             }
18373             this.store = new Roo.data.SimpleStore({
18374                 'id': 0,
18375                 fields: ['value', 'text'],
18376                 data : d
18377             });
18378             this.valueField = 'value';
18379             this.displayField = 'text';
18380         }
18381         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18382         if(!this.lazyRender){
18383             this.target = true;
18384             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18385             s.parentNode.removeChild(s); // remove it
18386             this.render(this.el.parentNode);
18387         }else{
18388             s.parentNode.removeChild(s); // remove it
18389         }
18390
18391     }
18392     if (this.store) {
18393         this.store = Roo.factory(this.store, Roo.data);
18394     }
18395     
18396     this.selectedIndex = -1;
18397     if(this.mode == 'local'){
18398         if(config.queryDelay === undefined){
18399             this.queryDelay = 10;
18400         }
18401         if(config.minChars === undefined){
18402             this.minChars = 0;
18403         }
18404     }
18405 };
18406
18407 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18408     /**
18409      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18410      */
18411     /**
18412      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18413      * rendering into an Roo.Editor, defaults to false)
18414      */
18415     /**
18416      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18417      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18418      */
18419     /**
18420      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18421      */
18422     /**
18423      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18424      * the dropdown list (defaults to undefined, with no header element)
18425      */
18426
18427      /**
18428      * @cfg {String/Roo.Template} tpl The template to use to render the output
18429      */
18430      
18431     // private
18432     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18433     /**
18434      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18435      */
18436     listWidth: undefined,
18437     /**
18438      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18439      * mode = 'remote' or 'text' if mode = 'local')
18440      */
18441     displayField: undefined,
18442     /**
18443      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18444      * mode = 'remote' or 'value' if mode = 'local'). 
18445      * Note: use of a valueField requires the user make a selection
18446      * in order for a value to be mapped.
18447      */
18448     valueField: undefined,
18449     
18450     
18451     /**
18452      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18453      * field's data value (defaults to the underlying DOM element's name)
18454      */
18455     hiddenName: undefined,
18456     /**
18457      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18458      */
18459     listClass: '',
18460     /**
18461      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18462      */
18463     selectedClass: 'x-combo-selected',
18464     /**
18465      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18466      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18467      * which displays a downward arrow icon).
18468      */
18469     triggerClass : 'x-form-arrow-trigger',
18470     /**
18471      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18472      */
18473     shadow:'sides',
18474     /**
18475      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18476      * anchor positions (defaults to 'tl-bl')
18477      */
18478     listAlign: 'tl-bl?',
18479     /**
18480      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18481      */
18482     maxHeight: 300,
18483     /**
18484      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18485      * query specified by the allQuery config option (defaults to 'query')
18486      */
18487     triggerAction: 'query',
18488     /**
18489      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18490      * (defaults to 4, does not apply if editable = false)
18491      */
18492     minChars : 4,
18493     /**
18494      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18495      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18496      */
18497     typeAhead: false,
18498     /**
18499      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18500      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18501      */
18502     queryDelay: 500,
18503     /**
18504      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18505      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18506      */
18507     pageSize: 0,
18508     /**
18509      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18510      * when editable = true (defaults to false)
18511      */
18512     selectOnFocus:false,
18513     /**
18514      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18515      */
18516     queryParam: 'query',
18517     /**
18518      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18519      * when mode = 'remote' (defaults to 'Loading...')
18520      */
18521     loadingText: 'Loading...',
18522     /**
18523      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18524      */
18525     resizable: false,
18526     /**
18527      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18528      */
18529     handleHeight : 8,
18530     /**
18531      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18532      * traditional select (defaults to true)
18533      */
18534     editable: true,
18535     /**
18536      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18537      */
18538     allQuery: '',
18539     /**
18540      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18541      */
18542     mode: 'remote',
18543     /**
18544      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18545      * listWidth has a higher value)
18546      */
18547     minListWidth : 70,
18548     /**
18549      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18550      * allow the user to set arbitrary text into the field (defaults to false)
18551      */
18552     forceSelection:false,
18553     /**
18554      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18555      * if typeAhead = true (defaults to 250)
18556      */
18557     typeAheadDelay : 250,
18558     /**
18559      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18560      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18561      */
18562     valueNotFoundText : undefined,
18563     /**
18564      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18565      */
18566     blockFocus : false,
18567     
18568     /**
18569      * @cfg {Boolean} disableClear Disable showing of clear button.
18570      */
18571     disableClear : false,
18572     /**
18573      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18574      */
18575     alwaysQuery : false,
18576     
18577     //private
18578     addicon : false,
18579     editicon: false,
18580     
18581     // element that contains real text value.. (when hidden is used..)
18582      
18583     // private
18584     onRender : function(ct, position)
18585     {
18586         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18587         
18588         if(this.hiddenName){
18589             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18590                     'before', true);
18591             this.hiddenField.value =
18592                 this.hiddenValue !== undefined ? this.hiddenValue :
18593                 this.value !== undefined ? this.value : '';
18594
18595             // prevent input submission
18596             this.el.dom.removeAttribute('name');
18597              
18598              
18599         }
18600         
18601         if(Roo.isGecko){
18602             this.el.dom.setAttribute('autocomplete', 'off');
18603         }
18604
18605         var cls = 'x-combo-list';
18606
18607         this.list = new Roo.Layer({
18608             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18609         });
18610
18611         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18612         this.list.setWidth(lw);
18613         this.list.swallowEvent('mousewheel');
18614         this.assetHeight = 0;
18615
18616         if(this.title){
18617             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18618             this.assetHeight += this.header.getHeight();
18619         }
18620
18621         this.innerList = this.list.createChild({cls:cls+'-inner'});
18622         this.innerList.on('mouseover', this.onViewOver, this);
18623         this.innerList.on('mousemove', this.onViewMove, this);
18624         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18625         
18626         if(this.allowBlank && !this.pageSize && !this.disableClear){
18627             this.footer = this.list.createChild({cls:cls+'-ft'});
18628             this.pageTb = new Roo.Toolbar(this.footer);
18629            
18630         }
18631         if(this.pageSize){
18632             this.footer = this.list.createChild({cls:cls+'-ft'});
18633             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18634                     {pageSize: this.pageSize});
18635             
18636         }
18637         
18638         if (this.pageTb && this.allowBlank && !this.disableClear) {
18639             var _this = this;
18640             this.pageTb.add(new Roo.Toolbar.Fill(), {
18641                 cls: 'x-btn-icon x-btn-clear',
18642                 text: '&#160;',
18643                 handler: function()
18644                 {
18645                     _this.collapse();
18646                     _this.clearValue();
18647                     _this.onSelect(false, -1);
18648                 }
18649             });
18650         }
18651         if (this.footer) {
18652             this.assetHeight += this.footer.getHeight();
18653         }
18654         
18655
18656         if(!this.tpl){
18657             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18658         }
18659
18660         this.view = new Roo.View(this.innerList, this.tpl, {
18661             singleSelect:true,
18662             store: this.store,
18663             selectedClass: this.selectedClass
18664         });
18665
18666         this.view.on('click', this.onViewClick, this);
18667
18668         this.store.on('beforeload', this.onBeforeLoad, this);
18669         this.store.on('load', this.onLoad, this);
18670         this.store.on('loadexception', this.onLoadException, this);
18671
18672         if(this.resizable){
18673             this.resizer = new Roo.Resizable(this.list,  {
18674                pinned:true, handles:'se'
18675             });
18676             this.resizer.on('resize', function(r, w, h){
18677                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18678                 this.listWidth = w;
18679                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18680                 this.restrictHeight();
18681             }, this);
18682             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18683         }
18684         if(!this.editable){
18685             this.editable = true;
18686             this.setEditable(false);
18687         }  
18688         
18689         
18690         if (typeof(this.events.add.listeners) != 'undefined') {
18691             
18692             this.addicon = this.wrap.createChild(
18693                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18694        
18695             this.addicon.on('click', function(e) {
18696                 this.fireEvent('add', this);
18697             }, this);
18698         }
18699         if (typeof(this.events.edit.listeners) != 'undefined') {
18700             
18701             this.editicon = this.wrap.createChild(
18702                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18703             if (this.addicon) {
18704                 this.editicon.setStyle('margin-left', '40px');
18705             }
18706             this.editicon.on('click', function(e) {
18707                 
18708                 // we fire even  if inothing is selected..
18709                 this.fireEvent('edit', this, this.lastData );
18710                 
18711             }, this);
18712         }
18713         
18714         
18715         
18716     },
18717
18718     // private
18719     initEvents : function(){
18720         Roo.form.ComboBox.superclass.initEvents.call(this);
18721
18722         this.keyNav = new Roo.KeyNav(this.el, {
18723             "up" : function(e){
18724                 this.inKeyMode = true;
18725                 this.selectPrev();
18726             },
18727
18728             "down" : function(e){
18729                 if(!this.isExpanded()){
18730                     this.onTriggerClick();
18731                 }else{
18732                     this.inKeyMode = true;
18733                     this.selectNext();
18734                 }
18735             },
18736
18737             "enter" : function(e){
18738                 this.onViewClick();
18739                 //return true;
18740             },
18741
18742             "esc" : function(e){
18743                 this.collapse();
18744             },
18745
18746             "tab" : function(e){
18747                 this.onViewClick(false);
18748                 this.fireEvent("specialkey", this, e);
18749                 return true;
18750             },
18751
18752             scope : this,
18753
18754             doRelay : function(foo, bar, hname){
18755                 if(hname == 'down' || this.scope.isExpanded()){
18756                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18757                 }
18758                 return true;
18759             },
18760
18761             forceKeyDown: true
18762         });
18763         this.queryDelay = Math.max(this.queryDelay || 10,
18764                 this.mode == 'local' ? 10 : 250);
18765         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18766         if(this.typeAhead){
18767             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18768         }
18769         if(this.editable !== false){
18770             this.el.on("keyup", this.onKeyUp, this);
18771         }
18772         if(this.forceSelection){
18773             this.on('blur', this.doForce, this);
18774         }
18775     },
18776
18777     onDestroy : function(){
18778         if(this.view){
18779             this.view.setStore(null);
18780             this.view.el.removeAllListeners();
18781             this.view.el.remove();
18782             this.view.purgeListeners();
18783         }
18784         if(this.list){
18785             this.list.destroy();
18786         }
18787         if(this.store){
18788             this.store.un('beforeload', this.onBeforeLoad, this);
18789             this.store.un('load', this.onLoad, this);
18790             this.store.un('loadexception', this.onLoadException, this);
18791         }
18792         Roo.form.ComboBox.superclass.onDestroy.call(this);
18793     },
18794
18795     // private
18796     fireKey : function(e){
18797         if(e.isNavKeyPress() && !this.list.isVisible()){
18798             this.fireEvent("specialkey", this, e);
18799         }
18800     },
18801
18802     // private
18803     onResize: function(w, h){
18804         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18805         
18806         if(typeof w != 'number'){
18807             // we do not handle it!?!?
18808             return;
18809         }
18810         var tw = this.trigger.getWidth();
18811         tw += this.addicon ? this.addicon.getWidth() : 0;
18812         tw += this.editicon ? this.editicon.getWidth() : 0;
18813         var x = w - tw;
18814         this.el.setWidth( this.adjustWidth('input', x));
18815             
18816         this.trigger.setStyle('left', x+'px');
18817         
18818         if(this.list && this.listWidth === undefined){
18819             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18820             this.list.setWidth(lw);
18821             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18822         }
18823         
18824     
18825         
18826     },
18827
18828     /**
18829      * Allow or prevent the user from directly editing the field text.  If false is passed,
18830      * the user will only be able to select from the items defined in the dropdown list.  This method
18831      * is the runtime equivalent of setting the 'editable' config option at config time.
18832      * @param {Boolean} value True to allow the user to directly edit the field text
18833      */
18834     setEditable : function(value){
18835         if(value == this.editable){
18836             return;
18837         }
18838         this.editable = value;
18839         if(!value){
18840             this.el.dom.setAttribute('readOnly', true);
18841             this.el.on('mousedown', this.onTriggerClick,  this);
18842             this.el.addClass('x-combo-noedit');
18843         }else{
18844             this.el.dom.setAttribute('readOnly', false);
18845             this.el.un('mousedown', this.onTriggerClick,  this);
18846             this.el.removeClass('x-combo-noedit');
18847         }
18848     },
18849
18850     // private
18851     onBeforeLoad : function(){
18852         if(!this.hasFocus){
18853             return;
18854         }
18855         this.innerList.update(this.loadingText ?
18856                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18857         this.restrictHeight();
18858         this.selectedIndex = -1;
18859     },
18860
18861     // private
18862     onLoad : function(){
18863         if(!this.hasFocus){
18864             return;
18865         }
18866         if(this.store.getCount() > 0){
18867             this.expand();
18868             this.restrictHeight();
18869             if(this.lastQuery == this.allQuery){
18870                 if(this.editable){
18871                     this.el.dom.select();
18872                 }
18873                 if(!this.selectByValue(this.value, true)){
18874                     this.select(0, true);
18875                 }
18876             }else{
18877                 this.selectNext();
18878                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18879                     this.taTask.delay(this.typeAheadDelay);
18880                 }
18881             }
18882         }else{
18883             this.onEmptyResults();
18884         }
18885         //this.el.focus();
18886     },
18887     // private
18888     onLoadException : function()
18889     {
18890         this.collapse();
18891         Roo.log(this.store.reader.jsonData);
18892         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18893             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18894         }
18895         
18896         
18897     },
18898     // private
18899     onTypeAhead : function(){
18900         if(this.store.getCount() > 0){
18901             var r = this.store.getAt(0);
18902             var newValue = r.data[this.displayField];
18903             var len = newValue.length;
18904             var selStart = this.getRawValue().length;
18905             if(selStart != len){
18906                 this.setRawValue(newValue);
18907                 this.selectText(selStart, newValue.length);
18908             }
18909         }
18910     },
18911
18912     // private
18913     onSelect : function(record, index){
18914         if(this.fireEvent('beforeselect', this, record, index) !== false){
18915             this.setFromData(index > -1 ? record.data : false);
18916             this.collapse();
18917             this.fireEvent('select', this, record, index);
18918         }
18919     },
18920
18921     /**
18922      * Returns the currently selected field value or empty string if no value is set.
18923      * @return {String} value The selected value
18924      */
18925     getValue : function(){
18926         if(this.valueField){
18927             return typeof this.value != 'undefined' ? this.value : '';
18928         }
18929         return Roo.form.ComboBox.superclass.getValue.call(this);
18930     },
18931
18932     /**
18933      * Clears any text/value currently set in the field
18934      */
18935     clearValue : function(){
18936         if(this.hiddenField){
18937             this.hiddenField.value = '';
18938         }
18939         this.value = '';
18940         this.setRawValue('');
18941         this.lastSelectionText = '';
18942         
18943     },
18944
18945     /**
18946      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18947      * will be displayed in the field.  If the value does not match the data value of an existing item,
18948      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18949      * Otherwise the field will be blank (although the value will still be set).
18950      * @param {String} value The value to match
18951      */
18952     setValue : function(v){
18953         var text = v;
18954         if(this.valueField){
18955             var r = this.findRecord(this.valueField, v);
18956             if(r){
18957                 text = r.data[this.displayField];
18958             }else if(this.valueNotFoundText !== undefined){
18959                 text = this.valueNotFoundText;
18960             }
18961         }
18962         this.lastSelectionText = text;
18963         if(this.hiddenField){
18964             this.hiddenField.value = v;
18965         }
18966         Roo.form.ComboBox.superclass.setValue.call(this, text);
18967         this.value = v;
18968     },
18969     /**
18970      * @property {Object} the last set data for the element
18971      */
18972     
18973     lastData : false,
18974     /**
18975      * Sets the value of the field based on a object which is related to the record format for the store.
18976      * @param {Object} value the value to set as. or false on reset?
18977      */
18978     setFromData : function(o){
18979         var dv = ''; // display value
18980         var vv = ''; // value value..
18981         this.lastData = o;
18982         if (this.displayField) {
18983             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18984         } else {
18985             // this is an error condition!!!
18986             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18987         }
18988         
18989         if(this.valueField){
18990             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18991         }
18992         if(this.hiddenField){
18993             this.hiddenField.value = vv;
18994             
18995             this.lastSelectionText = dv;
18996             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18997             this.value = vv;
18998             return;
18999         }
19000         // no hidden field.. - we store the value in 'value', but still display
19001         // display field!!!!
19002         this.lastSelectionText = dv;
19003         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19004         this.value = vv;
19005         
19006         
19007     },
19008     // private
19009     reset : function(){
19010         // overridden so that last data is reset..
19011         this.setValue(this.resetValue);
19012         this.originalValue = this.getValue();
19013         this.clearInvalid();
19014         this.lastData = false;
19015         if (this.view) {
19016             this.view.clearSelections();
19017         }
19018     },
19019     // private
19020     findRecord : function(prop, value){
19021         var record;
19022         if(this.store.getCount() > 0){
19023             this.store.each(function(r){
19024                 if(r.data[prop] == value){
19025                     record = r;
19026                     return false;
19027                 }
19028                 return true;
19029             });
19030         }
19031         return record;
19032     },
19033     
19034     getName: function()
19035     {
19036         // returns hidden if it's set..
19037         if (!this.rendered) {return ''};
19038         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19039         
19040     },
19041     // private
19042     onViewMove : function(e, t){
19043         this.inKeyMode = false;
19044     },
19045
19046     // private
19047     onViewOver : function(e, t){
19048         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19049             return;
19050         }
19051         var item = this.view.findItemFromChild(t);
19052         if(item){
19053             var index = this.view.indexOf(item);
19054             this.select(index, false);
19055         }
19056     },
19057
19058     // private
19059     onViewClick : function(doFocus)
19060     {
19061         var index = this.view.getSelectedIndexes()[0];
19062         var r = this.store.getAt(index);
19063         if(r){
19064             this.onSelect(r, index);
19065         }
19066         if(doFocus !== false && !this.blockFocus){
19067             this.el.focus();
19068         }
19069     },
19070
19071     // private
19072     restrictHeight : function(){
19073         this.innerList.dom.style.height = '';
19074         var inner = this.innerList.dom;
19075         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19076         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19077         this.list.beginUpdate();
19078         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19079         this.list.alignTo(this.el, this.listAlign);
19080         this.list.endUpdate();
19081     },
19082
19083     // private
19084     onEmptyResults : function(){
19085         this.collapse();
19086     },
19087
19088     /**
19089      * Returns true if the dropdown list is expanded, else false.
19090      */
19091     isExpanded : function(){
19092         return this.list.isVisible();
19093     },
19094
19095     /**
19096      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19097      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19098      * @param {String} value The data value of the item to select
19099      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19100      * selected item if it is not currently in view (defaults to true)
19101      * @return {Boolean} True if the value matched an item in the list, else false
19102      */
19103     selectByValue : function(v, scrollIntoView){
19104         if(v !== undefined && v !== null){
19105             var r = this.findRecord(this.valueField || this.displayField, v);
19106             if(r){
19107                 this.select(this.store.indexOf(r), scrollIntoView);
19108                 return true;
19109             }
19110         }
19111         return false;
19112     },
19113
19114     /**
19115      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19116      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19117      * @param {Number} index The zero-based index of the list item to select
19118      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19119      * selected item if it is not currently in view (defaults to true)
19120      */
19121     select : function(index, scrollIntoView){
19122         this.selectedIndex = index;
19123         this.view.select(index);
19124         if(scrollIntoView !== false){
19125             var el = this.view.getNode(index);
19126             if(el){
19127                 this.innerList.scrollChildIntoView(el, false);
19128             }
19129         }
19130     },
19131
19132     // private
19133     selectNext : function(){
19134         var ct = this.store.getCount();
19135         if(ct > 0){
19136             if(this.selectedIndex == -1){
19137                 this.select(0);
19138             }else if(this.selectedIndex < ct-1){
19139                 this.select(this.selectedIndex+1);
19140             }
19141         }
19142     },
19143
19144     // private
19145     selectPrev : function(){
19146         var ct = this.store.getCount();
19147         if(ct > 0){
19148             if(this.selectedIndex == -1){
19149                 this.select(0);
19150             }else if(this.selectedIndex != 0){
19151                 this.select(this.selectedIndex-1);
19152             }
19153         }
19154     },
19155
19156     // private
19157     onKeyUp : function(e){
19158         if(this.editable !== false && !e.isSpecialKey()){
19159             this.lastKey = e.getKey();
19160             this.dqTask.delay(this.queryDelay);
19161         }
19162     },
19163
19164     // private
19165     validateBlur : function(){
19166         return !this.list || !this.list.isVisible();   
19167     },
19168
19169     // private
19170     initQuery : function(){
19171         this.doQuery(this.getRawValue());
19172     },
19173
19174     // private
19175     doForce : function(){
19176         if(this.el.dom.value.length > 0){
19177             this.el.dom.value =
19178                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19179              
19180         }
19181     },
19182
19183     /**
19184      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19185      * query allowing the query action to be canceled if needed.
19186      * @param {String} query The SQL query to execute
19187      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19188      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19189      * saved in the current store (defaults to false)
19190      */
19191     doQuery : function(q, forceAll){
19192         if(q === undefined || q === null){
19193             q = '';
19194         }
19195         var qe = {
19196             query: q,
19197             forceAll: forceAll,
19198             combo: this,
19199             cancel:false
19200         };
19201         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19202             return false;
19203         }
19204         q = qe.query;
19205         forceAll = qe.forceAll;
19206         if(forceAll === true || (q.length >= this.minChars)){
19207             if(this.lastQuery != q || this.alwaysQuery){
19208                 this.lastQuery = q;
19209                 if(this.mode == 'local'){
19210                     this.selectedIndex = -1;
19211                     if(forceAll){
19212                         this.store.clearFilter();
19213                     }else{
19214                         this.store.filter(this.displayField, q);
19215                     }
19216                     this.onLoad();
19217                 }else{
19218                     this.store.baseParams[this.queryParam] = q;
19219                     this.store.load({
19220                         params: this.getParams(q)
19221                     });
19222                     this.expand();
19223                 }
19224             }else{
19225                 this.selectedIndex = -1;
19226                 this.onLoad();   
19227             }
19228         }
19229     },
19230
19231     // private
19232     getParams : function(q){
19233         var p = {};
19234         //p[this.queryParam] = q;
19235         if(this.pageSize){
19236             p.start = 0;
19237             p.limit = this.pageSize;
19238         }
19239         return p;
19240     },
19241
19242     /**
19243      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19244      */
19245     collapse : function(){
19246         if(!this.isExpanded()){
19247             return;
19248         }
19249         this.list.hide();
19250         Roo.get(document).un('mousedown', this.collapseIf, this);
19251         Roo.get(document).un('mousewheel', this.collapseIf, this);
19252         if (!this.editable) {
19253             Roo.get(document).un('keydown', this.listKeyPress, this);
19254         }
19255         this.fireEvent('collapse', this);
19256     },
19257
19258     // private
19259     collapseIf : function(e){
19260         if(!e.within(this.wrap) && !e.within(this.list)){
19261             this.collapse();
19262         }
19263     },
19264
19265     /**
19266      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19267      */
19268     expand : function(){
19269         if(this.isExpanded() || !this.hasFocus){
19270             return;
19271         }
19272         this.list.alignTo(this.el, this.listAlign);
19273         this.list.show();
19274         Roo.get(document).on('mousedown', this.collapseIf, this);
19275         Roo.get(document).on('mousewheel', this.collapseIf, this);
19276         if (!this.editable) {
19277             Roo.get(document).on('keydown', this.listKeyPress, this);
19278         }
19279         
19280         this.fireEvent('expand', this);
19281     },
19282
19283     // private
19284     // Implements the default empty TriggerField.onTriggerClick function
19285     onTriggerClick : function(){
19286         if(this.disabled){
19287             return;
19288         }
19289         if(this.isExpanded()){
19290             this.collapse();
19291             if (!this.blockFocus) {
19292                 this.el.focus();
19293             }
19294             
19295         }else {
19296             this.hasFocus = true;
19297             if(this.triggerAction == 'all') {
19298                 this.doQuery(this.allQuery, true);
19299             } else {
19300                 this.doQuery(this.getRawValue());
19301             }
19302             if (!this.blockFocus) {
19303                 this.el.focus();
19304             }
19305         }
19306     },
19307     listKeyPress : function(e)
19308     {
19309         //Roo.log('listkeypress');
19310         // scroll to first matching element based on key pres..
19311         if (e.isSpecialKey()) {
19312             return false;
19313         }
19314         var k = String.fromCharCode(e.getKey()).toUpperCase();
19315         //Roo.log(k);
19316         var match  = false;
19317         var csel = this.view.getSelectedNodes();
19318         var cselitem = false;
19319         if (csel.length) {
19320             var ix = this.view.indexOf(csel[0]);
19321             cselitem  = this.store.getAt(ix);
19322             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19323                 cselitem = false;
19324             }
19325             
19326         }
19327         
19328         this.store.each(function(v) { 
19329             if (cselitem) {
19330                 // start at existing selection.
19331                 if (cselitem.id == v.id) {
19332                     cselitem = false;
19333                 }
19334                 return;
19335             }
19336                 
19337             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19338                 match = this.store.indexOf(v);
19339                 return false;
19340             }
19341         }, this);
19342         
19343         if (match === false) {
19344             return true; // no more action?
19345         }
19346         // scroll to?
19347         this.view.select(match);
19348         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19349         sn.scrollIntoView(sn.dom.parentNode, false);
19350     } 
19351
19352     /** 
19353     * @cfg {Boolean} grow 
19354     * @hide 
19355     */
19356     /** 
19357     * @cfg {Number} growMin 
19358     * @hide 
19359     */
19360     /** 
19361     * @cfg {Number} growMax 
19362     * @hide 
19363     */
19364     /**
19365      * @hide
19366      * @method autoSize
19367      */
19368 });/*
19369  * Copyright(c) 2010-2012, Roo J Solutions Limited
19370  *
19371  * Licence LGPL
19372  *
19373  */
19374
19375 /**
19376  * @class Roo.form.ComboBoxArray
19377  * @extends Roo.form.TextField
19378  * A facebook style adder... for lists of email / people / countries  etc...
19379  * pick multiple items from a combo box, and shows each one.
19380  *
19381  *  Fred [x]  Brian [x]  [Pick another |v]
19382  *
19383  *
19384  *  For this to work: it needs various extra information
19385  *    - normal combo problay has
19386  *      name, hiddenName
19387  *    + displayField, valueField
19388  *
19389  *    For our purpose...
19390  *
19391  *
19392  *   If we change from 'extends' to wrapping...
19393  *   
19394  *  
19395  *
19396  
19397  
19398  * @constructor
19399  * Create a new ComboBoxArray.
19400  * @param {Object} config Configuration options
19401  */
19402  
19403
19404 Roo.form.ComboBoxArray = function(config)
19405 {
19406     this.addEvents({
19407         /**
19408          * @event beforeremove
19409          * Fires before remove the value from the list
19410              * @param {Roo.form.ComboBoxArray} _self This combo box array
19411              * @param {Roo.form.ComboBoxArray.Item} item removed item
19412              */
19413         'beforeremove' : true,
19414         /**
19415          * @event remove
19416          * Fires when remove the value from the list
19417              * @param {Roo.form.ComboBoxArray} _self This combo box array
19418              * @param {Roo.form.ComboBoxArray.Item} item removed item
19419              */
19420         'remove' : true
19421         
19422         
19423     });
19424     
19425     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19426     
19427     this.items = new Roo.util.MixedCollection(false);
19428     
19429     // construct the child combo...
19430     
19431     
19432     
19433     
19434    
19435     
19436 }
19437
19438  
19439 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19440
19441     /**
19442      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19443      */
19444     
19445     lastData : false,
19446     
19447     // behavies liek a hiddne field
19448     inputType:      'hidden',
19449     /**
19450      * @cfg {Number} width The width of the box that displays the selected element
19451      */ 
19452     width:          300,
19453
19454     
19455     
19456     /**
19457      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19458      */
19459     name : false,
19460     /**
19461      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19462      */
19463     hiddenName : false,
19464       /**
19465      * @cfg {String} seperator    The value seperator normally ',' 
19466      */
19467     seperator : ',',
19468     
19469     // private the array of items that are displayed..
19470     items  : false,
19471     // private - the hidden field el.
19472     hiddenEl : false,
19473     // private - the filed el..
19474     el : false,
19475     
19476     //validateValue : function() { return true; }, // all values are ok!
19477     //onAddClick: function() { },
19478     
19479     onRender : function(ct, position) 
19480     {
19481         
19482         // create the standard hidden element
19483         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19484         
19485         
19486         // give fake names to child combo;
19487         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19488         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19489         
19490         this.combo = Roo.factory(this.combo, Roo.form);
19491         this.combo.onRender(ct, position);
19492         if (typeof(this.combo.width) != 'undefined') {
19493             this.combo.onResize(this.combo.width,0);
19494         }
19495         
19496         this.combo.initEvents();
19497         
19498         // assigned so form know we need to do this..
19499         this.store          = this.combo.store;
19500         this.valueField     = this.combo.valueField;
19501         this.displayField   = this.combo.displayField ;
19502         
19503         
19504         this.combo.wrap.addClass('x-cbarray-grp');
19505         
19506         var cbwrap = this.combo.wrap.createChild(
19507             {tag: 'div', cls: 'x-cbarray-cb'},
19508             this.combo.el.dom
19509         );
19510         
19511              
19512         this.hiddenEl = this.combo.wrap.createChild({
19513             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19514         });
19515         this.el = this.combo.wrap.createChild({
19516             tag: 'input',  type:'hidden' , name: this.name, value : ''
19517         });
19518          //   this.el.dom.removeAttribute("name");
19519         
19520         
19521         this.outerWrap = this.combo.wrap;
19522         this.wrap = cbwrap;
19523         
19524         this.outerWrap.setWidth(this.width);
19525         this.outerWrap.dom.removeChild(this.el.dom);
19526         
19527         this.wrap.dom.appendChild(this.el.dom);
19528         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19529         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19530         
19531         this.combo.trigger.setStyle('position','relative');
19532         this.combo.trigger.setStyle('left', '0px');
19533         this.combo.trigger.setStyle('top', '2px');
19534         
19535         this.combo.el.setStyle('vertical-align', 'text-bottom');
19536         
19537         //this.trigger.setStyle('vertical-align', 'top');
19538         
19539         // this should use the code from combo really... on('add' ....)
19540         if (this.adder) {
19541             
19542         
19543             this.adder = this.outerWrap.createChild(
19544                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19545             var _t = this;
19546             this.adder.on('click', function(e) {
19547                 _t.fireEvent('adderclick', this, e);
19548             }, _t);
19549         }
19550         //var _t = this;
19551         //this.adder.on('click', this.onAddClick, _t);
19552         
19553         
19554         this.combo.on('select', function(cb, rec, ix) {
19555             this.addItem(rec.data);
19556             
19557             cb.setValue('');
19558             cb.el.dom.value = '';
19559             //cb.lastData = rec.data;
19560             // add to list
19561             
19562         }, this);
19563         
19564         
19565     },
19566     
19567     
19568     getName: function()
19569     {
19570         // returns hidden if it's set..
19571         if (!this.rendered) {return ''};
19572         return  this.hiddenName ? this.hiddenName : this.name;
19573         
19574     },
19575     
19576     
19577     onResize: function(w, h){
19578         
19579         return;
19580         // not sure if this is needed..
19581         //this.combo.onResize(w,h);
19582         
19583         if(typeof w != 'number'){
19584             // we do not handle it!?!?
19585             return;
19586         }
19587         var tw = this.combo.trigger.getWidth();
19588         tw += this.addicon ? this.addicon.getWidth() : 0;
19589         tw += this.editicon ? this.editicon.getWidth() : 0;
19590         var x = w - tw;
19591         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19592             
19593         this.combo.trigger.setStyle('left', '0px');
19594         
19595         if(this.list && this.listWidth === undefined){
19596             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19597             this.list.setWidth(lw);
19598             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19599         }
19600         
19601     
19602         
19603     },
19604     
19605     addItem: function(rec)
19606     {
19607         var valueField = this.combo.valueField;
19608         var displayField = this.combo.displayField;
19609         
19610         if (this.items.indexOfKey(rec[valueField]) > -1) {
19611             //console.log("GOT " + rec.data.id);
19612             return;
19613         }
19614         
19615         var x = new Roo.form.ComboBoxArray.Item({
19616             //id : rec[this.idField],
19617             data : rec,
19618             displayField : displayField ,
19619             tipField : displayField ,
19620             cb : this
19621         });
19622         // use the 
19623         this.items.add(rec[valueField],x);
19624         // add it before the element..
19625         this.updateHiddenEl();
19626         x.render(this.outerWrap, this.wrap.dom);
19627         // add the image handler..
19628     },
19629     
19630     updateHiddenEl : function()
19631     {
19632         this.validate();
19633         if (!this.hiddenEl) {
19634             return;
19635         }
19636         var ar = [];
19637         var idField = this.combo.valueField;
19638         
19639         this.items.each(function(f) {
19640             ar.push(f.data[idField]);
19641         });
19642         this.hiddenEl.dom.value = ar.join(this.seperator);
19643         this.validate();
19644     },
19645     
19646     reset : function()
19647     {
19648         this.items.clear();
19649         
19650         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19651            el.remove();
19652         });
19653         
19654         this.el.dom.value = '';
19655         if (this.hiddenEl) {
19656             this.hiddenEl.dom.value = '';
19657         }
19658         
19659     },
19660     getValue: function()
19661     {
19662         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19663     },
19664     setValue: function(v) // not a valid action - must use addItems..
19665     {
19666         
19667         this.reset();
19668          
19669         if (this.store.isLocal && (typeof(v) == 'string')) {
19670             // then we can use the store to find the values..
19671             // comma seperated at present.. this needs to allow JSON based encoding..
19672             this.hiddenEl.value  = v;
19673             var v_ar = [];
19674             Roo.each(v.split(this.seperator), function(k) {
19675                 Roo.log("CHECK " + this.valueField + ',' + k);
19676                 var li = this.store.query(this.valueField, k);
19677                 if (!li.length) {
19678                     return;
19679                 }
19680                 var add = {};
19681                 add[this.valueField] = k;
19682                 add[this.displayField] = li.item(0).data[this.displayField];
19683                 
19684                 this.addItem(add);
19685             }, this) 
19686              
19687         }
19688         if (typeof(v) == 'object' ) {
19689             // then let's assume it's an array of objects..
19690             Roo.each(v, function(l) {
19691                 var add = l;
19692                 if (typeof(l) == 'string') {
19693                     add = {};
19694                     add[this.valueField] = l;
19695                     add[this.displayField] = l
19696                 }
19697                 this.addItem(add);
19698             }, this);
19699              
19700         }
19701         
19702         
19703     },
19704     setFromData: function(v)
19705     {
19706         // this recieves an object, if setValues is called.
19707         this.reset();
19708         this.el.dom.value = v[this.displayField];
19709         this.hiddenEl.dom.value = v[this.valueField];
19710         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19711             return;
19712         }
19713         var kv = v[this.valueField];
19714         var dv = v[this.displayField];
19715         kv = typeof(kv) != 'string' ? '' : kv;
19716         dv = typeof(dv) != 'string' ? '' : dv;
19717         
19718         
19719         var keys = kv.split(this.seperator);
19720         var display = dv.split(this.seperator);
19721         for (var i = 0 ; i < keys.length; i++) {
19722             add = {};
19723             add[this.valueField] = keys[i];
19724             add[this.displayField] = display[i];
19725             this.addItem(add);
19726         }
19727       
19728         
19729     },
19730     
19731     /**
19732      * Validates the combox array value
19733      * @return {Boolean} True if the value is valid, else false
19734      */
19735     validate : function(){
19736         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19737             this.clearInvalid();
19738             return true;
19739         }
19740         return false;
19741     },
19742     
19743     validateValue : function(value){
19744         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19745         
19746     },
19747     
19748     /*@
19749      * overide
19750      * 
19751      */
19752     isDirty : function() {
19753         if(this.disabled) {
19754             return false;
19755         }
19756         
19757         try {
19758             var d = Roo.decode(String(this.originalValue));
19759         } catch (e) {
19760             return String(this.getValue()) !== String(this.originalValue);
19761         }
19762         
19763         var originalValue = [];
19764         
19765         for (var i = 0; i < d.length; i++){
19766             originalValue.push(d[i][this.valueField]);
19767         }
19768         
19769         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19770         
19771     }
19772     
19773 });
19774
19775
19776
19777 /**
19778  * @class Roo.form.ComboBoxArray.Item
19779  * @extends Roo.BoxComponent
19780  * A selected item in the list
19781  *  Fred [x]  Brian [x]  [Pick another |v]
19782  * 
19783  * @constructor
19784  * Create a new item.
19785  * @param {Object} config Configuration options
19786  */
19787  
19788 Roo.form.ComboBoxArray.Item = function(config) {
19789     config.id = Roo.id();
19790     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19791 }
19792
19793 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19794     data : {},
19795     cb: false,
19796     displayField : false,
19797     tipField : false,
19798     
19799     
19800     defaultAutoCreate : {
19801         tag: 'div',
19802         cls: 'x-cbarray-item',
19803         cn : [ 
19804             { tag: 'div' },
19805             {
19806                 tag: 'img',
19807                 width:16,
19808                 height : 16,
19809                 src : Roo.BLANK_IMAGE_URL ,
19810                 align: 'center'
19811             }
19812         ]
19813         
19814     },
19815     
19816  
19817     onRender : function(ct, position)
19818     {
19819         Roo.form.Field.superclass.onRender.call(this, ct, position);
19820         
19821         if(!this.el){
19822             var cfg = this.getAutoCreate();
19823             this.el = ct.createChild(cfg, position);
19824         }
19825         
19826         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19827         
19828         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19829             this.cb.renderer(this.data) :
19830             String.format('{0}',this.data[this.displayField]);
19831         
19832             
19833         this.el.child('div').dom.setAttribute('qtip',
19834                         String.format('{0}',this.data[this.tipField])
19835         );
19836         
19837         this.el.child('img').on('click', this.remove, this);
19838         
19839     },
19840    
19841     remove : function()
19842     {
19843         if(this.cb.disabled){
19844             return;
19845         }
19846         
19847         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19848             this.cb.items.remove(this);
19849             this.el.child('img').un('click', this.remove, this);
19850             this.el.remove();
19851             this.cb.updateHiddenEl();
19852
19853             this.cb.fireEvent('remove', this.cb, this);
19854         }
19855         
19856     }
19857 });/*
19858  * RooJS Library 1.1.1
19859  * Copyright(c) 2008-2011  Alan Knowles
19860  *
19861  * License - LGPL
19862  */
19863  
19864
19865 /**
19866  * @class Roo.form.ComboNested
19867  * @extends Roo.form.ComboBox
19868  * A combobox for that allows selection of nested items in a list,
19869  * eg.
19870  *
19871  *  Book
19872  *    -> red
19873  *    -> green
19874  *  Table
19875  *    -> square
19876  *      ->red
19877  *      ->green
19878  *    -> rectangle
19879  *      ->green
19880  *      
19881  * 
19882  * @constructor
19883  * Create a new ComboNested
19884  * @param {Object} config Configuration options
19885  */
19886 Roo.form.ComboNested = function(config){
19887     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19888     // should verify some data...
19889     // like
19890     // hiddenName = required..
19891     // displayField = required
19892     // valudField == required
19893     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19894     var _t = this;
19895     Roo.each(req, function(e) {
19896         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19897             throw "Roo.form.ComboNested : missing value for: " + e;
19898         }
19899     });
19900      
19901     
19902 };
19903
19904 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19905    
19906     /*
19907      * @config {Number} max Number of columns to show
19908      */
19909     
19910     maxColumns : 3,
19911    
19912     list : null, // the outermost div..
19913     innerLists : null, // the
19914     views : null,
19915     stores : null,
19916     // private
19917     loadingChildren : false,
19918     
19919     onRender : function(ct, position)
19920     {
19921         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19922         
19923         if(this.hiddenName){
19924             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19925                     'before', true);
19926             this.hiddenField.value =
19927                 this.hiddenValue !== undefined ? this.hiddenValue :
19928                 this.value !== undefined ? this.value : '';
19929
19930             // prevent input submission
19931             this.el.dom.removeAttribute('name');
19932              
19933              
19934         }
19935         
19936         if(Roo.isGecko){
19937             this.el.dom.setAttribute('autocomplete', 'off');
19938         }
19939
19940         var cls = 'x-combo-list';
19941
19942         this.list = new Roo.Layer({
19943             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19944         });
19945
19946         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19947         this.list.setWidth(lw);
19948         this.list.swallowEvent('mousewheel');
19949         this.assetHeight = 0;
19950
19951         if(this.title){
19952             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19953             this.assetHeight += this.header.getHeight();
19954         }
19955         this.innerLists = [];
19956         this.views = [];
19957         this.stores = [];
19958         for (var i =0 ; i < this.maxColumns; i++) {
19959             this.onRenderList( cls, i);
19960         }
19961         
19962         // always needs footer, as we are going to have an 'OK' button.
19963         this.footer = this.list.createChild({cls:cls+'-ft'});
19964         this.pageTb = new Roo.Toolbar(this.footer);  
19965         var _this = this;
19966         this.pageTb.add(  {
19967             
19968             text: 'Done',
19969             handler: function()
19970             {
19971                 _this.collapse();
19972             }
19973         });
19974         
19975         if ( this.allowBlank && !this.disableClear) {
19976             
19977             this.pageTb.add(new Roo.Toolbar.Fill(), {
19978                 cls: 'x-btn-icon x-btn-clear',
19979                 text: '&#160;',
19980                 handler: function()
19981                 {
19982                     _this.collapse();
19983                     _this.clearValue();
19984                     _this.onSelect(false, -1);
19985                 }
19986             });
19987         }
19988         if (this.footer) {
19989             this.assetHeight += this.footer.getHeight();
19990         }
19991         
19992     },
19993     onRenderList : function (  cls, i)
19994     {
19995         
19996         var lw = Math.floor(
19997                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19998         );
19999         
20000         this.list.setWidth(lw); // default to '1'
20001
20002         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20003         //il.on('mouseover', this.onViewOver, this, { list:  i });
20004         //il.on('mousemove', this.onViewMove, this, { list:  i });
20005         il.setWidth(lw);
20006         il.setStyle({ 'overflow-x' : 'hidden'});
20007
20008         if(!this.tpl){
20009             this.tpl = new Roo.Template({
20010                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20011                 isEmpty: function (value, allValues) {
20012                     //Roo.log(value);
20013                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20014                     return dl ? 'has-children' : 'no-children'
20015                 }
20016             });
20017         }
20018         
20019         var store  = this.store;
20020         if (i > 0) {
20021             store  = new Roo.data.SimpleStore({
20022                 //fields : this.store.reader.meta.fields,
20023                 reader : this.store.reader,
20024                 data : [ ]
20025             });
20026         }
20027         this.stores[i]  = store;
20028                   
20029         var view = this.views[i] = new Roo.View(
20030             il,
20031             this.tpl,
20032             {
20033                 singleSelect:true,
20034                 store: store,
20035                 selectedClass: this.selectedClass
20036             }
20037         );
20038         view.getEl().setWidth(lw);
20039         view.getEl().setStyle({
20040             position: i < 1 ? 'relative' : 'absolute',
20041             top: 0,
20042             left: (i * lw ) + 'px',
20043             display : i > 0 ? 'none' : 'block'
20044         });
20045         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20046         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20047         //view.on('click', this.onViewClick, this, { list : i });
20048
20049         store.on('beforeload', this.onBeforeLoad, this);
20050         store.on('load',  this.onLoad, this, { list  : i});
20051         store.on('loadexception', this.onLoadException, this);
20052
20053         // hide the other vies..
20054         
20055         
20056         
20057     },
20058       
20059     restrictHeight : function()
20060     {
20061         var mh = 0;
20062         Roo.each(this.innerLists, function(il,i) {
20063             var el = this.views[i].getEl();
20064             el.dom.style.height = '';
20065             var inner = el.dom;
20066             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20067             // only adjust heights on other ones..
20068             mh = Math.max(h, mh);
20069             if (i < 1) {
20070                 
20071                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20072                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20073                
20074             }
20075             
20076             
20077         }, this);
20078         
20079         this.list.beginUpdate();
20080         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20081         this.list.alignTo(this.el, this.listAlign);
20082         this.list.endUpdate();
20083         
20084     },
20085      
20086     
20087     // -- store handlers..
20088     // private
20089     onBeforeLoad : function()
20090     {
20091         if(!this.hasFocus){
20092             return;
20093         }
20094         this.innerLists[0].update(this.loadingText ?
20095                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20096         this.restrictHeight();
20097         this.selectedIndex = -1;
20098     },
20099     // private
20100     onLoad : function(a,b,c,d)
20101     {
20102         if (!this.loadingChildren) {
20103             // then we are loading the top level. - hide the children
20104             for (var i = 1;i < this.views.length; i++) {
20105                 this.views[i].getEl().setStyle({ display : 'none' });
20106             }
20107             var lw = Math.floor(
20108                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20109             );
20110         
20111              this.list.setWidth(lw); // default to '1'
20112
20113             
20114         }
20115         if(!this.hasFocus){
20116             return;
20117         }
20118         
20119         if(this.store.getCount() > 0) {
20120             this.expand();
20121             this.restrictHeight();   
20122         } else {
20123             this.onEmptyResults();
20124         }
20125         
20126         if (!this.loadingChildren) {
20127             this.selectActive();
20128         }
20129         /*
20130         this.stores[1].loadData([]);
20131         this.stores[2].loadData([]);
20132         this.views
20133         */    
20134     
20135         //this.el.focus();
20136     },
20137     
20138     
20139     // private
20140     onLoadException : function()
20141     {
20142         this.collapse();
20143         Roo.log(this.store.reader.jsonData);
20144         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20145             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20146         }
20147         
20148         
20149     },
20150     // no cleaning of leading spaces on blur here.
20151     cleanLeadingSpace : function(e) { },
20152     
20153
20154     onSelectChange : function (view, sels, opts )
20155     {
20156         var ix = view.getSelectedIndexes();
20157          
20158         if (opts.list > this.maxColumns - 2) {
20159             if (view.store.getCount()<  1) {
20160                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20161
20162             } else  {
20163                 if (ix.length) {
20164                     // used to clear ?? but if we are loading unselected 
20165                     this.setFromData(view.store.getAt(ix[0]).data);
20166                 }
20167                 
20168             }
20169             
20170             return;
20171         }
20172         
20173         if (!ix.length) {
20174             // this get's fired when trigger opens..
20175            // this.setFromData({});
20176             var str = this.stores[opts.list+1];
20177             str.data.clear(); // removeall wihtout the fire events..
20178             return;
20179         }
20180         
20181         var rec = view.store.getAt(ix[0]);
20182          
20183         this.setFromData(rec.data);
20184         this.fireEvent('select', this, rec, ix[0]);
20185         
20186         var lw = Math.floor(
20187              (
20188                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20189              ) / this.maxColumns
20190         );
20191         this.loadingChildren = true;
20192         this.stores[opts.list+1].loadDataFromChildren( rec );
20193         this.loadingChildren = false;
20194         var dl = this.stores[opts.list+1]. getTotalCount();
20195         
20196         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20197         
20198         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20199         for (var i = opts.list+2; i < this.views.length;i++) {
20200             this.views[i].getEl().setStyle({ display : 'none' });
20201         }
20202         
20203         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20204         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20205         
20206         if (this.isLoading) {
20207            // this.selectActive(opts.list);
20208         }
20209          
20210     },
20211     
20212     
20213     
20214     
20215     onDoubleClick : function()
20216     {
20217         this.collapse(); //??
20218     },
20219     
20220      
20221     
20222     
20223     
20224     // private
20225     recordToStack : function(store, prop, value, stack)
20226     {
20227         var cstore = new Roo.data.SimpleStore({
20228             //fields : this.store.reader.meta.fields, // we need array reader.. for
20229             reader : this.store.reader,
20230             data : [ ]
20231         });
20232         var _this = this;
20233         var record  = false;
20234         var srec = false;
20235         if(store.getCount() < 1){
20236             return false;
20237         }
20238         store.each(function(r){
20239             if(r.data[prop] == value){
20240                 record = r;
20241             srec = r;
20242                 return false;
20243             }
20244             if (r.data.cn && r.data.cn.length) {
20245                 cstore.loadDataFromChildren( r);
20246                 var cret = _this.recordToStack(cstore, prop, value, stack);
20247                 if (cret !== false) {
20248                     record = cret;
20249                     srec = r;
20250                     return false;
20251                 }
20252             }
20253              
20254             return true;
20255         });
20256         if (record == false) {
20257             return false
20258         }
20259         stack.unshift(srec);
20260         return record;
20261     },
20262     
20263     /*
20264      * find the stack of stores that match our value.
20265      *
20266      * 
20267      */
20268     
20269     selectActive : function ()
20270     {
20271         // if store is not loaded, then we will need to wait for that to happen first.
20272         var stack = [];
20273         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20274         for (var i = 0; i < stack.length; i++ ) {
20275             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20276         }
20277         
20278     }
20279         
20280          
20281     
20282     
20283     
20284     
20285 });/*
20286  * Based on:
20287  * Ext JS Library 1.1.1
20288  * Copyright(c) 2006-2007, Ext JS, LLC.
20289  *
20290  * Originally Released Under LGPL - original licence link has changed is not relivant.
20291  *
20292  * Fork - LGPL
20293  * <script type="text/javascript">
20294  */
20295 /**
20296  * @class Roo.form.Checkbox
20297  * @extends Roo.form.Field
20298  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20299  * @constructor
20300  * Creates a new Checkbox
20301  * @param {Object} config Configuration options
20302  */
20303 Roo.form.Checkbox = function(config){
20304     Roo.form.Checkbox.superclass.constructor.call(this, config);
20305     this.addEvents({
20306         /**
20307          * @event check
20308          * Fires when the checkbox is checked or unchecked.
20309              * @param {Roo.form.Checkbox} this This checkbox
20310              * @param {Boolean} checked The new checked value
20311              */
20312         check : true
20313     });
20314 };
20315
20316 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20317     /**
20318      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20319      */
20320     focusClass : undefined,
20321     /**
20322      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20323      */
20324     fieldClass: "x-form-field",
20325     /**
20326      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20327      */
20328     checked: false,
20329     /**
20330      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20331      * {tag: "input", type: "checkbox", autocomplete: "off"})
20332      */
20333     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20334     /**
20335      * @cfg {String} boxLabel The text that appears beside the checkbox
20336      */
20337     boxLabel : "",
20338     /**
20339      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20340      */  
20341     inputValue : '1',
20342     /**
20343      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20344      */
20345      valueOff: '0', // value when not checked..
20346
20347     actionMode : 'viewEl', 
20348     //
20349     // private
20350     itemCls : 'x-menu-check-item x-form-item',
20351     groupClass : 'x-menu-group-item',
20352     inputType : 'hidden',
20353     
20354     
20355     inSetChecked: false, // check that we are not calling self...
20356     
20357     inputElement: false, // real input element?
20358     basedOn: false, // ????
20359     
20360     isFormField: true, // not sure where this is needed!!!!
20361
20362     onResize : function(){
20363         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20364         if(!this.boxLabel){
20365             this.el.alignTo(this.wrap, 'c-c');
20366         }
20367     },
20368
20369     initEvents : function(){
20370         Roo.form.Checkbox.superclass.initEvents.call(this);
20371         this.el.on("click", this.onClick,  this);
20372         this.el.on("change", this.onClick,  this);
20373     },
20374
20375
20376     getResizeEl : function(){
20377         return this.wrap;
20378     },
20379
20380     getPositionEl : function(){
20381         return this.wrap;
20382     },
20383
20384     // private
20385     onRender : function(ct, position){
20386         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20387         /*
20388         if(this.inputValue !== undefined){
20389             this.el.dom.value = this.inputValue;
20390         }
20391         */
20392         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20393         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20394         var viewEl = this.wrap.createChild({ 
20395             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20396         this.viewEl = viewEl;   
20397         this.wrap.on('click', this.onClick,  this); 
20398         
20399         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20400         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20401         
20402         
20403         
20404         if(this.boxLabel){
20405             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20406         //    viewEl.on('click', this.onClick,  this); 
20407         }
20408         //if(this.checked){
20409             this.setChecked(this.checked);
20410         //}else{
20411             //this.checked = this.el.dom;
20412         //}
20413
20414     },
20415
20416     // private
20417     initValue : Roo.emptyFn,
20418
20419     /**
20420      * Returns the checked state of the checkbox.
20421      * @return {Boolean} True if checked, else false
20422      */
20423     getValue : function(){
20424         if(this.el){
20425             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20426         }
20427         return this.valueOff;
20428         
20429     },
20430
20431         // private
20432     onClick : function(){ 
20433         if (this.disabled) {
20434             return;
20435         }
20436         this.setChecked(!this.checked);
20437
20438         //if(this.el.dom.checked != this.checked){
20439         //    this.setValue(this.el.dom.checked);
20440        // }
20441     },
20442
20443     /**
20444      * Sets the checked state of the checkbox.
20445      * On is always based on a string comparison between inputValue and the param.
20446      * @param {Boolean/String} value - the value to set 
20447      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20448      */
20449     setValue : function(v,suppressEvent){
20450         
20451         
20452         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20453         //if(this.el && this.el.dom){
20454         //    this.el.dom.checked = this.checked;
20455         //    this.el.dom.defaultChecked = this.checked;
20456         //}
20457         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20458         //this.fireEvent("check", this, this.checked);
20459     },
20460     // private..
20461     setChecked : function(state,suppressEvent)
20462     {
20463         if (this.inSetChecked) {
20464             this.checked = state;
20465             return;
20466         }
20467         
20468     
20469         if(this.wrap){
20470             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20471         }
20472         this.checked = state;
20473         if(suppressEvent !== true){
20474             this.fireEvent('check', this, state);
20475         }
20476         this.inSetChecked = true;
20477         this.el.dom.value = state ? this.inputValue : this.valueOff;
20478         this.inSetChecked = false;
20479         
20480     },
20481     // handle setting of hidden value by some other method!!?!?
20482     setFromHidden: function()
20483     {
20484         if(!this.el){
20485             return;
20486         }
20487         //console.log("SET FROM HIDDEN");
20488         //alert('setFrom hidden');
20489         this.setValue(this.el.dom.value);
20490     },
20491     
20492     onDestroy : function()
20493     {
20494         if(this.viewEl){
20495             Roo.get(this.viewEl).remove();
20496         }
20497          
20498         Roo.form.Checkbox.superclass.onDestroy.call(this);
20499     },
20500     
20501     setBoxLabel : function(str)
20502     {
20503         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20504     }
20505
20506 });/*
20507  * Based on:
20508  * Ext JS Library 1.1.1
20509  * Copyright(c) 2006-2007, Ext JS, LLC.
20510  *
20511  * Originally Released Under LGPL - original licence link has changed is not relivant.
20512  *
20513  * Fork - LGPL
20514  * <script type="text/javascript">
20515  */
20516  
20517 /**
20518  * @class Roo.form.Radio
20519  * @extends Roo.form.Checkbox
20520  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20521  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20522  * @constructor
20523  * Creates a new Radio
20524  * @param {Object} config Configuration options
20525  */
20526 Roo.form.Radio = function(){
20527     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20528 };
20529 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20530     inputType: 'radio',
20531
20532     /**
20533      * If this radio is part of a group, it will return the selected value
20534      * @return {String}
20535      */
20536     getGroupValue : function(){
20537         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20538     },
20539     
20540     
20541     onRender : function(ct, position){
20542         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20543         
20544         if(this.inputValue !== undefined){
20545             this.el.dom.value = this.inputValue;
20546         }
20547          
20548         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20549         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20550         //var viewEl = this.wrap.createChild({ 
20551         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20552         //this.viewEl = viewEl;   
20553         //this.wrap.on('click', this.onClick,  this); 
20554         
20555         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20556         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20557         
20558         
20559         
20560         if(this.boxLabel){
20561             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20562         //    viewEl.on('click', this.onClick,  this); 
20563         }
20564          if(this.checked){
20565             this.el.dom.checked =   'checked' ;
20566         }
20567          
20568     } 
20569     
20570     
20571 });Roo.rtf = {}; // namespace
20572 Roo.rtf.Hex = function(hex)
20573 {
20574     this.hexstr = hex;
20575 };
20576 Roo.rtf.Paragraph = function(opts)
20577 {
20578     this.content = []; ///??? is that used?
20579 };Roo.rtf.Span = function(opts)
20580 {
20581     this.value = opts.value;
20582 };
20583
20584 Roo.rtf.Group = function(parent)
20585 {
20586     // we dont want to acutally store parent - it will make debug a nightmare..
20587     this.content = [];
20588     this.cn  = [];
20589      
20590        
20591     
20592 };
20593
20594 Roo.rtf.Group.prototype = {
20595     ignorable : false,
20596     content: false,
20597     cn: false,
20598     addContent : function(node) {
20599         // could set styles...
20600         this.content.push(node);
20601     },
20602     addChild : function(cn)
20603     {
20604         this.cn.push(cn);
20605     },
20606     // only for images really...
20607     toDataURL : function()
20608     {
20609         var mimetype = false;
20610         switch(true) {
20611             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20612                 mimetype = "image/png";
20613                 break;
20614              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20615                 mimetype = "image/jpeg";
20616                 break;
20617             default :
20618                 return 'about:blank'; // ?? error?
20619         }
20620         
20621         
20622         var hexstring = this.content[this.content.length-1].value;
20623         
20624         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20625             return String.fromCharCode(parseInt(a, 16));
20626         }).join(""));
20627     }
20628     
20629 };
20630 // this looks like it's normally the {rtf{ .... }}
20631 Roo.rtf.Document = function()
20632 {
20633     // we dont want to acutally store parent - it will make debug a nightmare..
20634     this.rtlch  = [];
20635     this.content = [];
20636     this.cn = [];
20637     
20638 };
20639 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20640     addChild : function(cn)
20641     {
20642         this.cn.push(cn);
20643         switch(cn.type) {
20644             case 'rtlch': // most content seems to be inside this??
20645             case 'listtext':
20646             case 'shpinst':
20647                 this.rtlch.push(cn);
20648                 return;
20649             default:
20650                 this[cn.type] = cn;
20651         }
20652         
20653     },
20654     
20655     getElementsByType : function(type)
20656     {
20657         var ret =  [];
20658         this._getElementsByType(type, ret, this.cn, 'rtf');
20659         return ret;
20660     },
20661     _getElementsByType : function (type, ret, search_array, path)
20662     {
20663         search_array.forEach(function(n,i) {
20664             if (n.type == type) {
20665                 n.path = path + '/' + n.type + ':' + i;
20666                 ret.push(n);
20667             }
20668             if (n.cn.length > 0) {
20669                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20670             }
20671         },this);
20672     }
20673     
20674 });
20675  
20676 Roo.rtf.Ctrl = function(opts)
20677 {
20678     this.value = opts.value;
20679     this.param = opts.param;
20680 };
20681 /**
20682  *
20683  *
20684  * based on this https://github.com/iarna/rtf-parser
20685  * it's really only designed to extract pict from pasted RTF 
20686  *
20687  * usage:
20688  *
20689  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20690  *  
20691  *
20692  */
20693
20694  
20695
20696
20697
20698 Roo.rtf.Parser = function(text) {
20699     //super({objectMode: true})
20700     this.text = '';
20701     this.parserState = this.parseText;
20702     
20703     // these are for interpeter...
20704     this.doc = {};
20705     ///this.parserState = this.parseTop
20706     this.groupStack = [];
20707     this.hexStore = [];
20708     this.doc = false;
20709     
20710     this.groups = []; // where we put the return.
20711     
20712     for (var ii = 0; ii < text.length; ++ii) {
20713         ++this.cpos;
20714         
20715         if (text[ii] === '\n') {
20716             ++this.row;
20717             this.col = 1;
20718         } else {
20719             ++this.col;
20720         }
20721         this.parserState(text[ii]);
20722     }
20723     
20724     
20725     
20726 };
20727 Roo.rtf.Parser.prototype = {
20728     text : '', // string being parsed..
20729     controlWord : '',
20730     controlWordParam :  '',
20731     hexChar : '',
20732     doc : false,
20733     group: false,
20734     groupStack : false,
20735     hexStore : false,
20736     
20737     
20738     cpos : 0, 
20739     row : 1, // reportin?
20740     col : 1, //
20741
20742      
20743     push : function (el)
20744     {
20745         var m = 'cmd'+ el.type;
20746         if (typeof(this[m]) == 'undefined') {
20747             Roo.log('invalid cmd:' + el.type);
20748             return;
20749         }
20750         this[m](el);
20751         //Roo.log(el);
20752     },
20753     flushHexStore : function()
20754     {
20755         if (this.hexStore.length < 1) {
20756             return;
20757         }
20758         var hexstr = this.hexStore.map(
20759             function(cmd) {
20760                 return cmd.value;
20761         }).join('');
20762         
20763         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20764               
20765             
20766         this.hexStore.splice(0)
20767         
20768     },
20769     
20770     cmdgroupstart : function()
20771     {
20772         this.flushHexStore();
20773         if (this.group) {
20774             this.groupStack.push(this.group);
20775         }
20776          // parent..
20777         if (this.doc === false) {
20778             this.group = this.doc = new Roo.rtf.Document();
20779             return;
20780             
20781         }
20782         this.group = new Roo.rtf.Group(this.group);
20783     },
20784     cmdignorable : function()
20785     {
20786         this.flushHexStore();
20787         this.group.ignorable = true;
20788     },
20789     cmdendparagraph : function()
20790     {
20791         this.flushHexStore();
20792         this.group.addContent(new Roo.rtf.Paragraph());
20793     },
20794     cmdgroupend : function ()
20795     {
20796         this.flushHexStore();
20797         var endingGroup = this.group;
20798         
20799         
20800         this.group = this.groupStack.pop();
20801         if (this.group) {
20802             this.group.addChild(endingGroup);
20803         }
20804         
20805         
20806         
20807         var doc = this.group || this.doc;
20808         //if (endingGroup instanceof FontTable) {
20809         //  doc.fonts = endingGroup.table
20810         //} else if (endingGroup instanceof ColorTable) {
20811         //  doc.colors = endingGroup.table
20812         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20813         if (endingGroup.ignorable === false) {
20814             //code
20815             this.groups.push(endingGroup);
20816            // Roo.log( endingGroup );
20817         }
20818             //Roo.each(endingGroup.content, function(item)) {
20819             //    doc.addContent(item);
20820             //}
20821             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20822         //}
20823     },
20824     cmdtext : function (cmd)
20825     {
20826         this.flushHexStore();
20827         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20828             //this.group = this.doc
20829         }
20830         this.group.addContent(new Roo.rtf.Span(cmd));
20831     },
20832     cmdcontrolword : function (cmd)
20833     {
20834         this.flushHexStore();
20835         if (!this.group.type) {
20836             this.group.type = cmd.value;
20837             return;
20838         }
20839         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20840         // we actually don't care about ctrl words...
20841         return ;
20842         /*
20843         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20844         if (this[method]) {
20845             this[method](cmd.param)
20846         } else {
20847             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20848         }
20849         */
20850     },
20851     cmdhexchar : function(cmd) {
20852         this.hexStore.push(cmd);
20853     },
20854     cmderror : function(cmd) {
20855         throw new Exception (cmd.value);
20856     },
20857     
20858     /*
20859       _flush (done) {
20860         if (this.text !== '\u0000') this.emitText()
20861         done()
20862       }
20863       */
20864       
20865       
20866     parseText : function(c)
20867     {
20868         if (c === '\\') {
20869             this.parserState = this.parseEscapes;
20870         } else if (c === '{') {
20871             this.emitStartGroup();
20872         } else if (c === '}') {
20873             this.emitEndGroup();
20874         } else if (c === '\x0A' || c === '\x0D') {
20875             // cr/lf are noise chars
20876         } else {
20877             this.text += c;
20878         }
20879     },
20880     
20881     parseEscapes: function (c)
20882     {
20883         if (c === '\\' || c === '{' || c === '}') {
20884             this.text += c;
20885             this.parserState = this.parseText;
20886         } else {
20887             this.parserState = this.parseControlSymbol;
20888             this.parseControlSymbol(c);
20889         }
20890     },
20891     parseControlSymbol: function(c)
20892     {
20893         if (c === '~') {
20894             this.text += '\u00a0'; // nbsp
20895             this.parserState = this.parseText
20896         } else if (c === '-') {
20897              this.text += '\u00ad'; // soft hyphen
20898         } else if (c === '_') {
20899             this.text += '\u2011'; // non-breaking hyphen
20900         } else if (c === '*') {
20901             this.emitIgnorable();
20902             this.parserState = this.parseText;
20903         } else if (c === "'") {
20904             this.parserState = this.parseHexChar;
20905         } else if (c === '|') { // formula cacter
20906             this.emitFormula();
20907             this.parserState = this.parseText;
20908         } else if (c === ':') { // subentry in an index entry
20909             this.emitIndexSubEntry();
20910             this.parserState = this.parseText;
20911         } else if (c === '\x0a') {
20912             this.emitEndParagraph();
20913             this.parserState = this.parseText;
20914         } else if (c === '\x0d') {
20915             this.emitEndParagraph();
20916             this.parserState = this.parseText;
20917         } else {
20918             this.parserState = this.parseControlWord;
20919             this.parseControlWord(c);
20920         }
20921     },
20922     parseHexChar: function (c)
20923     {
20924         if (/^[A-Fa-f0-9]$/.test(c)) {
20925             this.hexChar += c;
20926             if (this.hexChar.length >= 2) {
20927               this.emitHexChar();
20928               this.parserState = this.parseText;
20929             }
20930             return;
20931         }
20932         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20933         this.parserState = this.parseText;
20934         
20935     },
20936     parseControlWord : function(c)
20937     {
20938         if (c === ' ') {
20939             this.emitControlWord();
20940             this.parserState = this.parseText;
20941         } else if (/^[-\d]$/.test(c)) {
20942             this.parserState = this.parseControlWordParam;
20943             this.controlWordParam += c;
20944         } else if (/^[A-Za-z]$/.test(c)) {
20945           this.controlWord += c;
20946         } else {
20947           this.emitControlWord();
20948           this.parserState = this.parseText;
20949           this.parseText(c);
20950         }
20951     },
20952     parseControlWordParam : function (c) {
20953         if (/^\d$/.test(c)) {
20954           this.controlWordParam += c;
20955         } else if (c === ' ') {
20956           this.emitControlWord();
20957           this.parserState = this.parseText;
20958         } else {
20959           this.emitControlWord();
20960           this.parserState = this.parseText;
20961           this.parseText(c);
20962         }
20963     },
20964     
20965     
20966     
20967     
20968     emitText : function () {
20969         if (this.text === '') {
20970             return;
20971         }
20972         this.push({
20973             type: 'text',
20974             value: this.text,
20975             pos: this.cpos,
20976             row: this.row,
20977             col: this.col
20978         });
20979         this.text = ''
20980     },
20981     emitControlWord : function ()
20982     {
20983         this.emitText();
20984         if (this.controlWord === '') {
20985             this.emitError('empty control word');
20986         } else {
20987             this.push({
20988                   type: 'controlword',
20989                   value: this.controlWord,
20990                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
20991                   pos: this.cpos,
20992                   row: this.row,
20993                   col: this.col
20994             });
20995         }
20996         this.controlWord = '';
20997         this.controlWordParam = '';
20998     },
20999     emitStartGroup : function ()
21000     {
21001         this.emitText();
21002         this.push({
21003             type: 'groupstart',
21004             pos: this.cpos,
21005             row: this.row,
21006             col: this.col
21007         });
21008     },
21009     emitEndGroup : function ()
21010     {
21011         this.emitText();
21012         this.push({
21013             type: 'groupend',
21014             pos: this.cpos,
21015             row: this.row,
21016             col: this.col
21017         });
21018     },
21019     emitIgnorable : function ()
21020     {
21021         this.emitText();
21022         this.push({
21023             type: 'ignorable',
21024             pos: this.cpos,
21025             row: this.row,
21026             col: this.col
21027         });
21028     },
21029     emitHexChar : function ()
21030     {
21031         this.emitText();
21032         this.push({
21033             type: 'hexchar',
21034             value: this.hexChar,
21035             pos: this.cpos,
21036             row: this.row,
21037             col: this.col
21038         });
21039         this.hexChar = ''
21040     },
21041     emitError : function (message)
21042     {
21043       this.emitText();
21044       this.push({
21045             type: 'error',
21046             value: message,
21047             row: this.row,
21048             col: this.col,
21049             char: this.cpos //,
21050             //stack: new Error().stack
21051         });
21052     },
21053     emitEndParagraph : function () {
21054         this.emitText();
21055         this.push({
21056             type: 'endparagraph',
21057             pos: this.cpos,
21058             row: this.row,
21059             col: this.col
21060         });
21061     }
21062      
21063 } ;
21064 Roo.htmleditor = {};
21065  
21066 /**
21067  * @class Roo.htmleditor.Filter
21068  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21069  * @cfg {DomElement} node The node to iterate and filter
21070  * @cfg {boolean|String|Array} tag Tags to replace 
21071  * @constructor
21072  * Create a new Filter.
21073  * @param {Object} config Configuration options
21074  */
21075
21076
21077
21078 Roo.htmleditor.Filter = function(cfg) {
21079     Roo.apply(this.cfg);
21080     // this does not actually call walk as it's really just a abstract class
21081 }
21082
21083
21084 Roo.htmleditor.Filter.prototype = {
21085     
21086     node: false,
21087     
21088     tag: false,
21089
21090     // overrride to do replace comments.
21091     replaceComment : false,
21092     
21093     // overrride to do replace or do stuff with tags..
21094     replaceTag : false,
21095     
21096     walk : function(dom)
21097     {
21098         Roo.each( Array.from(dom.childNodes), function( e ) {
21099             switch(true) {
21100                 
21101                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
21102                     this.replaceComment(e);
21103                     return;
21104                 
21105                 case e.nodeType != 1: //not a node.
21106                     return;
21107                 
21108                 case this.tag === true: // everything
21109                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21110                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21111                     if (this.replaceTag && false === this.replaceTag(e)) {
21112                         return;
21113                     }
21114                     if (e.hasChildNodes()) {
21115                         this.walk(e);
21116                     }
21117                     return;
21118                 
21119                 default:    // tags .. that do not match.
21120                     if (e.hasChildNodes()) {
21121                         this.walk(e);
21122                     }
21123             }
21124             
21125         }, this);
21126         
21127     }
21128 }; 
21129
21130 /**
21131  * @class Roo.htmleditor.FilterAttributes
21132  * clean attributes and  styles including http:// etc.. in attribute
21133  * @constructor
21134 * Run a new Attribute Filter
21135 * @param {Object} config Configuration options
21136  */
21137 Roo.htmleditor.FilterAttributes = function(cfg)
21138 {
21139     Roo.apply(this, cfg);
21140     this.attrib_black = this.attrib_black || [];
21141     this.attrib_white = this.attrib_white || [];
21142
21143     this.attrib_clean = this.attrib_clean || [];
21144     this.style_white = this.style_white || [];
21145     this.style_black = this.style_black || [];
21146     this.walk(cfg.node);
21147 }
21148
21149 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21150 {
21151     tag: true, // all tags
21152     
21153     attrib_black : false, // array
21154     attrib_clean : false,
21155     attrib_white : false,
21156
21157     style_white : false,
21158     style_black : false,
21159      
21160      
21161     replaceTag : function(node)
21162     {
21163         if (!node.attributes || !node.attributes.length) {
21164             return true;
21165         }
21166         
21167         for (var i = node.attributes.length-1; i > -1 ; i--) {
21168             var a = node.attributes[i];
21169             //console.log(a);
21170             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21171                 node.removeAttribute(a.name);
21172                 continue;
21173             }
21174             
21175             
21176             
21177             if (a.name.toLowerCase().substr(0,2)=='on')  {
21178                 node.removeAttribute(a.name);
21179                 continue;
21180             }
21181             
21182             
21183             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21184                 node.removeAttribute(a.name);
21185                 continue;
21186             }
21187             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21188                 this.cleanAttr(node,a.name,a.value); // fixme..
21189                 continue;
21190             }
21191             if (a.name == 'style') {
21192                 this.cleanStyle(node,a.name,a.value);
21193                 continue;
21194             }
21195             /// clean up MS crap..
21196             // tecnically this should be a list of valid class'es..
21197             
21198             
21199             if (a.name == 'class') {
21200                 if (a.value.match(/^Mso/)) {
21201                     node.removeAttribute('class');
21202                 }
21203                 
21204                 if (a.value.match(/^body$/)) {
21205                     node.removeAttribute('class');
21206                 }
21207                 continue;
21208             }
21209             
21210             
21211             // style cleanup!?
21212             // class cleanup?
21213             
21214         }
21215         return true; // clean children
21216     },
21217         
21218     cleanAttr: function(node, n,v)
21219     {
21220         
21221         if (v.match(/^\./) || v.match(/^\//)) {
21222             return;
21223         }
21224         if (v.match(/^(http|https):\/\//)
21225             || v.match(/^mailto:/) 
21226             || v.match(/^ftp:/)
21227             || v.match(/^data:/)
21228             ) {
21229             return;
21230         }
21231         if (v.match(/^#/)) {
21232             return;
21233         }
21234         if (v.match(/^\{/)) { // allow template editing.
21235             return;
21236         }
21237 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21238         node.removeAttribute(n);
21239         
21240     },
21241     cleanStyle : function(node,  n,v)
21242     {
21243         if (v.match(/expression/)) { //XSS?? should we even bother..
21244             node.removeAttribute(n);
21245             return;
21246         }
21247         
21248         var parts = v.split(/;/);
21249         var clean = [];
21250         
21251         Roo.each(parts, function(p) {
21252             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21253             if (!p.length) {
21254                 return true;
21255             }
21256             var l = p.split(':').shift().replace(/\s+/g,'');
21257             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21258             
21259             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21260                 return true;
21261             }
21262             //Roo.log()
21263             // only allow 'c whitelisted system attributes'
21264             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21265                 return true;
21266             }
21267             
21268             
21269             clean.push(p);
21270             return true;
21271         },this);
21272         if (clean.length) { 
21273             node.setAttribute(n, clean.join(';'));
21274         } else {
21275             node.removeAttribute(n);
21276         }
21277         
21278     }
21279         
21280         
21281         
21282     
21283 });/**
21284  * @class Roo.htmleditor.FilterBlack
21285  * remove blacklisted elements.
21286  * @constructor
21287  * Run a new Blacklisted Filter
21288  * @param {Object} config Configuration options
21289  */
21290
21291 Roo.htmleditor.FilterBlack = function(cfg)
21292 {
21293     Roo.apply(this, cfg);
21294     this.walk(cfg.node);
21295 }
21296
21297 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21298 {
21299     tag : true, // all elements.
21300    
21301     replace : function(n)
21302     {
21303         n.parentNode.removeChild(n);
21304     }
21305 });
21306 /**
21307  * @class Roo.htmleditor.FilterComment
21308  * remove comments.
21309  * @constructor
21310 * Run a new Comments Filter
21311 * @param {Object} config Configuration options
21312  */
21313 Roo.htmleditor.FilterComment = function(cfg)
21314 {
21315     this.walk(cfg.node);
21316 }
21317
21318 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21319 {
21320   
21321     replaceComment : function(n)
21322     {
21323         n.parentNode.removeChild(n);
21324     }
21325 });/**
21326  * @class Roo.htmleditor.FilterKeepChildren
21327  * remove tags but keep children
21328  * @constructor
21329  * Run a new Keep Children Filter
21330  * @param {Object} config Configuration options
21331  */
21332
21333 Roo.htmleditor.FilterKeepChildren = function(cfg)
21334 {
21335     Roo.apply(this, cfg);
21336     if (this.tag === false) {
21337         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21338     }
21339     this.walk(cfg.node);
21340 }
21341
21342 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21343 {
21344     
21345   
21346     replaceTag : function(node)
21347     {
21348         // walk children...
21349         //Roo.log(node);
21350         var ar = Array.from(node.childNodes);
21351         //remove first..
21352         for (var i = 0; i < ar.length; i++) {
21353             if (ar[i].nodeType == 1) {
21354                 if (
21355                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21356                     || // array and it matches
21357                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21358                 ) {
21359                     this.replaceTag(ar[i]); // child is blacklisted as well...
21360                     continue;
21361                 }
21362             }
21363         }  
21364         ar = Array.from(node.childNodes);
21365         for (var i = 0; i < ar.length; i++) {
21366          
21367             node.removeChild(ar[i]);
21368             // what if we need to walk these???
21369             node.parentNode.insertBefore(ar[i], node);
21370             if (this.tag !== false) {
21371                 this.walk(ar[i]);
21372                 
21373             }
21374         }
21375         node.parentNode.removeChild(node);
21376         return false; // don't walk children
21377         
21378         
21379     }
21380 });/**
21381  * @class Roo.htmleditor.FilterParagraph
21382  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21383  * like on 'push' to remove the <p> tags and replace them with line breaks.
21384  * @constructor
21385  * Run a new Paragraph Filter
21386  * @param {Object} config Configuration options
21387  */
21388
21389 Roo.htmleditor.FilterParagraph = function(cfg)
21390 {
21391     // no need to apply config.
21392     this.walk(cfg.node);
21393 }
21394
21395 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21396 {
21397     
21398      
21399     tag : 'P',
21400     
21401      
21402     replaceTag : function(node)
21403     {
21404         
21405         if (node.childNodes.length == 1 &&
21406             node.childNodes[0].nodeType == 3 &&
21407             node.childNodes[0].textContent.trim().length < 1
21408             ) {
21409             // remove and replace with '<BR>';
21410             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21411             return false; // no need to walk..
21412         }
21413         var ar = Array.from(node.childNodes);
21414         for (var i = 0; i < ar.length; i++) {
21415             node.removeChild(ar[i]);
21416             // what if we need to walk these???
21417             node.parentNode.insertBefore(ar[i], node);
21418         }
21419         // now what about this?
21420         // <p> &nbsp; </p>
21421         
21422         // double BR.
21423         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21424         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21425         node.parentNode.removeChild(node);
21426         
21427         return false;
21428
21429     }
21430     
21431 });/**
21432  * @class Roo.htmleditor.FilterSpan
21433  * filter span's with no attributes out..
21434  * @constructor
21435  * Run a new Span Filter
21436  * @param {Object} config Configuration options
21437  */
21438
21439 Roo.htmleditor.FilterSpan = function(cfg)
21440 {
21441     // no need to apply config.
21442     this.walk(cfg.node);
21443 }
21444
21445 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21446 {
21447      
21448     tag : 'SPAN',
21449      
21450  
21451     replaceTag : function(node)
21452     {
21453         if (node.attributes && node.attributes.length > 0) {
21454             return true; // walk if there are any.
21455         }
21456         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21457         return false;
21458      
21459     }
21460     
21461 });/**
21462  * @class Roo.htmleditor.FilterTableWidth
21463   try and remove table width data - as that frequently messes up other stuff.
21464  * 
21465  *      was cleanTableWidths.
21466  *
21467  * Quite often pasting from word etc.. results in tables with column and widths.
21468  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21469  *
21470  * @constructor
21471  * Run a new Table Filter
21472  * @param {Object} config Configuration options
21473  */
21474
21475 Roo.htmleditor.FilterTableWidth = function(cfg)
21476 {
21477     // no need to apply config.
21478     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21479     this.walk(cfg.node);
21480 }
21481
21482 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21483 {
21484      
21485      
21486     
21487     replaceTag: function(node) {
21488         
21489         
21490       
21491         if (node.hasAttribute('width')) {
21492             node.removeAttribute('width');
21493         }
21494         
21495          
21496         if (node.hasAttribute("style")) {
21497             // pretty basic...
21498             
21499             var styles = node.getAttribute("style").split(";");
21500             var nstyle = [];
21501             Roo.each(styles, function(s) {
21502                 if (!s.match(/:/)) {
21503                     return;
21504                 }
21505                 var kv = s.split(":");
21506                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21507                     return;
21508                 }
21509                 // what ever is left... we allow.
21510                 nstyle.push(s);
21511             });
21512             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21513             if (!nstyle.length) {
21514                 node.removeAttribute('style');
21515             }
21516         }
21517         
21518         return true; // continue doing children..
21519     }
21520 });/**
21521  * @class Roo.htmleditor.FilterWord
21522  * try and clean up all the mess that Word generates.
21523  * 
21524  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21525  
21526  * @constructor
21527  * Run a new Span Filter
21528  * @param {Object} config Configuration options
21529  */
21530
21531 Roo.htmleditor.FilterWord = function(cfg)
21532 {
21533     // no need to apply config.
21534     this.walk(cfg.node);
21535 }
21536
21537 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21538 {
21539     tag: true,
21540      
21541     
21542     /**
21543      * Clean up MS wordisms...
21544      */
21545     replaceTag : function(node)
21546     {
21547          
21548         // no idea what this does - span with text, replaceds with just text.
21549         if(
21550                 node.nodeName == 'SPAN' &&
21551                 !node.hasAttributes() &&
21552                 node.childNodes.length == 1 &&
21553                 node.firstChild.nodeName == "#text"  
21554         ) {
21555             var textNode = node.firstChild;
21556             node.removeChild(textNode);
21557             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21558                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21559             }
21560             node.parentNode.insertBefore(textNode, node);
21561             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21562                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21563             }
21564             
21565             node.parentNode.removeChild(node);
21566             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21567         }
21568         
21569    
21570         
21571         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21572             node.parentNode.removeChild(node);
21573             return false; // dont do chidlren
21574         }
21575         //Roo.log(node.tagName);
21576         // remove - but keep children..
21577         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21578             //Roo.log('-- removed');
21579             while (node.childNodes.length) {
21580                 var cn = node.childNodes[0];
21581                 node.removeChild(cn);
21582                 node.parentNode.insertBefore(cn, node);
21583                 // move node to parent - and clean it..
21584                 this.replaceTag(cn);
21585             }
21586             node.parentNode.removeChild(node);
21587             /// no need to iterate chidlren = it's got none..
21588             //this.iterateChildren(node, this.cleanWord);
21589             return false; // no need to iterate children.
21590         }
21591         // clean styles
21592         if (node.className.length) {
21593             
21594             var cn = node.className.split(/\W+/);
21595             var cna = [];
21596             Roo.each(cn, function(cls) {
21597                 if (cls.match(/Mso[a-zA-Z]+/)) {
21598                     return;
21599                 }
21600                 cna.push(cls);
21601             });
21602             node.className = cna.length ? cna.join(' ') : '';
21603             if (!cna.length) {
21604                 node.removeAttribute("class");
21605             }
21606         }
21607         
21608         if (node.hasAttribute("lang")) {
21609             node.removeAttribute("lang");
21610         }
21611         
21612         if (node.hasAttribute("style")) {
21613             
21614             var styles = node.getAttribute("style").split(";");
21615             var nstyle = [];
21616             Roo.each(styles, function(s) {
21617                 if (!s.match(/:/)) {
21618                     return;
21619                 }
21620                 var kv = s.split(":");
21621                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21622                     return;
21623                 }
21624                 // what ever is left... we allow.
21625                 nstyle.push(s);
21626             });
21627             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21628             if (!nstyle.length) {
21629                 node.removeAttribute('style');
21630             }
21631         }
21632         return true; // do children
21633         
21634         
21635         
21636     }
21637 });
21638 /**
21639  * @class Roo.htmleditor.FilterStyleToTag
21640  * part of the word stuff... - certain 'styles' should be converted to tags.
21641  * eg.
21642  *   font-weight: bold -> bold
21643  *   ?? super / subscrit etc..
21644  * 
21645  * @constructor
21646 * Run a new style to tag filter.
21647 * @param {Object} config Configuration options
21648  */
21649 Roo.htmleditor.FilterStyleToTag = function(cfg)
21650 {
21651     
21652     this.tags = {
21653         B  : [ 'fontWeight' , 'bold'],
21654         I :  [ 'fontStyle' , 'italic'],
21655         //pre :  [ 'font-style' , 'italic'],
21656         // h1.. h6 ?? font-size?
21657         SUP : [ 'verticalAlign' , 'super' ],
21658         SUB : [ 'verticalAlign' , 'sub' ]
21659         
21660         
21661     };
21662     
21663     Roo.apply(this, cfg);
21664      
21665     
21666     this.walk(cfg.node);
21667     
21668     
21669     
21670 }
21671
21672
21673 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21674 {
21675     tag: true, // all tags
21676     
21677     tags : false,
21678     
21679     
21680     replaceTag : function(node)
21681     {
21682         
21683         
21684         if (node.getAttribute("style") === null) {
21685             return true;
21686         }
21687         var inject = [];
21688         for (var k in this.tags) {
21689             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21690                 inject.push(k);
21691                 node.style.removeProperty(this.tags[k][0]);
21692             }
21693         }
21694         if (!inject.length) {
21695             return true; 
21696         }
21697         var cn = Array.from(node.childNodes);
21698         var nn = node;
21699         Roo.each(inject, function(t) {
21700             var nc = node.ownerDocument.createelement(t);
21701             nn.appendChild(nc);
21702             nn = nc;
21703         });
21704         for(var i = 0;i < cn.length;cn++) {
21705             node.removeChild(cn[i]);
21706             nn.appendChild(cn[i]);
21707         }
21708         return true /// iterate thru
21709     }
21710     
21711 })/**
21712  * @class Roo.htmleditor.FilterLongBr
21713  * BR/BR/BR - keep a maximum of 2...
21714  * @constructor
21715  * Run a new Long BR Filter
21716  * @param {Object} config Configuration options
21717  */
21718
21719 Roo.htmleditor.FilterLongBr = function(cfg)
21720 {
21721     // no need to apply config.
21722     this.walk(cfg.node);
21723 }
21724
21725 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21726 {
21727     
21728      
21729     tag : 'BR',
21730     
21731      
21732     replaceTag : function(node)
21733     {
21734         
21735         var ps = node.nextSibling;
21736         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21737             ps = ps.nextSibling;
21738         }
21739         
21740         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21741             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21742             return false;
21743         }
21744         
21745         if (!ps || ps.nodeType != 1) {
21746             return false;
21747         }
21748         
21749         if (!ps || ps.tagName != 'BR') {
21750            
21751             return false;
21752         }
21753         
21754         
21755         
21756         
21757         
21758         if (!node.previousSibling) {
21759             return false;
21760         }
21761         var ps = node.previousSibling;
21762         
21763         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21764             ps = ps.previousSibling;
21765         }
21766         if (!ps || ps.nodeType != 1) {
21767             return false;
21768         }
21769         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21770         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21771             return false;
21772         }
21773         
21774         node.parentNode.removeChild(node); // remove me...
21775         
21776         return false; // no need to do children
21777
21778     }
21779     
21780 });
21781 /**
21782  * @class Roo.htmleditor.Tidy
21783  * Tidy HTML 
21784  * @cfg {Roo.HtmlEditorCore} core the editor.
21785  * @constructor
21786  * Create a new Filter.
21787  * @param {Object} config Configuration options
21788  */
21789
21790
21791 Roo.htmleditor.Tidy = function(cfg) {
21792     Roo.apply(this, cfg);
21793     
21794     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
21795      
21796 }
21797
21798 Roo.htmleditor.Tidy.toString = function(node)
21799 {
21800     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
21801 }
21802
21803 Roo.htmleditor.Tidy.prototype = {
21804     
21805     
21806     wrap : function(s) {
21807         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
21808     },
21809
21810     
21811     tidy : function(node, indent) {
21812      
21813         if  (node.nodeType == 3) {
21814             // text.
21815             
21816             
21817             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
21818                 
21819             
21820         }
21821         
21822         if  (node.nodeType != 1) {
21823             return '';
21824         }
21825         
21826         
21827         
21828         if (node.tagName == 'BODY') {
21829             
21830             return this.cn(node, '');
21831         }
21832              
21833              // Prints the node tagName, such as <A>, <IMG>, etc
21834         var ret = "<" + node.tagName +  this.attr(node) ;
21835         
21836         // elements with no children..
21837         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
21838                 return ret + '/>';
21839         }
21840         ret += '>';
21841         
21842         
21843         var cindent = indent === false ? '' : (indent + '  ');
21844         // tags where we will not pad the children.. (inline text tags etc..)
21845         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
21846             cindent = false;
21847             
21848             
21849         }
21850         
21851         var cn = this.cn(node, cindent );
21852         
21853         return ret + cn  + '</' + node.tagName + '>';
21854         
21855     },
21856     cn: function(node, indent)
21857     {
21858         var ret = [];
21859         
21860         var ar = Array.from(node.childNodes);
21861         for (var i = 0 ; i < ar.length ; i++) {
21862             
21863             
21864             
21865             if (indent !== false   // indent==false preservies everything
21866                 && i > 0
21867                 && ar[i].nodeType == 3 
21868                 && ar[i].nodeValue.length > 0
21869                 && ar[i].nodeValue.match(/^\s+/)
21870             ) {
21871                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
21872                     ret.pop(); // remove line break from last?
21873                 }
21874                 
21875                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
21876             }
21877             if (indent !== false
21878                 && ar[i].nodeType == 1 // element - and indent is not set... 
21879             ) {
21880                 ret.push("\n" + indent); 
21881             }
21882             
21883             ret.push(this.tidy(ar[i], indent));
21884             // text + trailing indent 
21885             if (indent !== false
21886                 && ar[i].nodeType == 3
21887                 && ar[i].nodeValue.length > 0
21888                 && ar[i].nodeValue.match(/\s+$/)
21889             ){
21890                 ret.push("\n" + indent); 
21891             }
21892             
21893             
21894             
21895             
21896         }
21897         // what if all text?
21898         
21899         
21900         return ret.join('');
21901     },
21902     
21903          
21904         
21905     attr : function(node)
21906     {
21907         var attr = [];
21908         for(i = 0; i < node.attributes.length;i++) {
21909             
21910             // skip empty values?
21911             if (!node.attributes.item(i).value.length) {
21912                 continue;
21913             }
21914             attr.push(  node.attributes.item(i).name + '="' +
21915                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
21916             );
21917         }
21918         return attr.length ? (' ' + attr.join(' ') ) : '';
21919         
21920     }
21921     
21922     
21923     
21924 }
21925 /**
21926  * @class Roo.htmleditor.KeyEnter
21927  * Handle Enter press..
21928  * @cfg {Roo.HtmlEditorCore} core the editor.
21929  * @constructor
21930  * Create a new Filter.
21931  * @param {Object} config Configuration options
21932  */
21933
21934
21935
21936 Roo.htmleditor.KeyEnter = function(cfg) {
21937     Roo.apply(this, cfg);
21938     // this does not actually call walk as it's really just a abstract class
21939  
21940     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
21941 }
21942
21943
21944 Roo.htmleditor.KeyEnter.prototype = {
21945     
21946     core : false,
21947     
21948     keypress : function(e) {
21949         if (e.charCode != 13) {
21950             return true;
21951         }
21952         e.preventDefault();
21953         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
21954         var doc = this.core.doc;
21955         
21956         var docFragment = doc.createDocumentFragment();
21957     
21958         //add a new line
21959         var newEle = doc.createTextNode('\n');
21960         docFragment.appendChild(newEle);
21961     
21962     
21963         var range = this.core.win.getSelection().getRangeAt(0);
21964         var n = range.commonAncestorContainer ;
21965         while (n && n.nodeType != 1) {
21966             n  = n.parentNode;
21967         }
21968         var li = false;
21969         if (n && n.tagName == 'UL') {
21970             li = doc.createElement('LI');
21971             n.appendChild(li);
21972             
21973         }
21974         if (n && n.tagName == 'LI') {
21975             li = doc.createElement('LI');
21976             if (n.nextSibling) {
21977                 n.parentNode.insertBefore(li, n.firstSibling);
21978                 
21979             } else {
21980                 n.parentNode.appendChild(li);
21981             }
21982         }
21983         if (li) {   
21984             range = doc.createRange();
21985             range.setStartAfter(li);
21986             range.collapse(true);
21987         
21988             //make the cursor there
21989             var sel = this.core.win.getSelection();
21990             sel.removeAllRanges();
21991             sel.addRange(range);
21992             return false;
21993             
21994             
21995         }
21996         //add the br, or p, or something else
21997         newEle = doc.createElement('br');
21998         docFragment.appendChild(newEle);
21999     
22000         //make the br replace selection
22001         
22002         range.deleteContents();
22003         
22004         range.insertNode(docFragment);
22005     
22006         //create a new range
22007         range = doc.createRange();
22008         range.setStartAfter(newEle);
22009         range.collapse(true);
22010     
22011         //make the cursor there
22012         var sel = this.core.win.getSelection();
22013         sel.removeAllRanges();
22014         sel.addRange(range);
22015     
22016         return false;
22017          
22018     }
22019 };
22020      
22021 /**
22022  * @class Roo.htmleditor.Block
22023  * Base class for html editor blocks - do not use it directly .. extend it..
22024  * @cfg {DomElement} node The node to apply stuff to.
22025  * @cfg {String} friendly_name the name that appears in the context bar about this block
22026  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
22027  
22028  * @constructor
22029  * Create a new Filter.
22030  * @param {Object} config Configuration options
22031  */
22032
22033 Roo.htmleditor.Block  = function(cfg)
22034 {
22035     // do nothing .. should not be called really.
22036 }
22037
22038 Roo.htmleditor.Block.factory = function(node)
22039 {
22040     
22041     var id = Roo.get(node).id;
22042     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
22043         Roo.htmleditor.Block.cache[id].readElement();
22044         return Roo.htmleditor.Block.cache[id];
22045     }
22046     
22047     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
22048     if (typeof(cls) == 'undefined') {
22049         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
22050         return false;
22051     }
22052     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
22053     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
22054 };
22055 // question goes here... do we need to clear out this cache sometimes?
22056 // or show we make it relivant to the htmleditor.
22057 Roo.htmleditor.Block.cache = {};
22058
22059 Roo.htmleditor.Block.prototype = {
22060     
22061     node : false,
22062     
22063      // used by context menu
22064     friendly_name : 'Image with caption',
22065     
22066     context : false,
22067     /**
22068      * Update a node with values from this object
22069      * @param {DomElement} node
22070      */
22071     updateElement : function(node)
22072     {
22073         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
22074     },
22075      /**
22076      * convert to plain HTML for calling insertAtCursor..
22077      */
22078     toHTML : function()
22079     {
22080         return Roo.DomHelper.markup(this.toObject());
22081     },
22082     /**
22083      * used by readEleemnt to extract data from a node
22084      * may need improving as it's pretty basic
22085      
22086      * @param {DomElement} node
22087      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
22088      * @param {String} attribute (use html - for contents, or style for using next param as style)
22089      * @param {String} style the style property - eg. text-align
22090      */
22091     getVal : function(node, tag, attr, style)
22092     {
22093         var n = node;
22094         if (tag !== true && n.tagName != tag.toUpperCase()) {
22095             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
22096             // but kiss for now.
22097             n = node.getElementsByTagName(tag).item(0);
22098         }
22099         if (attr == 'html') {
22100             return n.innerHTML;
22101         }
22102         if (attr == 'style') {
22103             return Roo.get(n).getStyle(style);
22104         }
22105         
22106         return Roo.get(n).attr(attr);
22107             
22108     },
22109     /**
22110      * create a DomHelper friendly object - for use with 
22111      * Roo.DomHelper.markup / overwrite / etc..
22112      * (override this)
22113      */
22114     toObject : function()
22115     {
22116         return {};
22117     },
22118       /**
22119      * Read a node that has a 'data-block' property - and extract the values from it.
22120      * @param {DomElement} node - the node
22121      */
22122     readElement : function(node)
22123     {
22124         
22125     } 
22126     
22127     
22128 };
22129
22130  
22131
22132 /**
22133  * @class Roo.htmleditor.BlockFigure
22134  * Block that has an image and a figcaption
22135  * @cfg {String} image_src the url for the image
22136  * @cfg {String} align (left|right) alignment for the block default left
22137  * @cfg {String} text_align (left|right) alignment for the text caption default left.
22138  * @cfg {String} caption the text to appear below  (and in the alt tag)
22139  * @cfg {String|number} image_width the width of the image number or %?
22140  * @cfg {String|number} image_height the height of the image number or %?
22141  * 
22142  * @constructor
22143  * Create a new Filter.
22144  * @param {Object} config Configuration options
22145  */
22146
22147 Roo.htmleditor.BlockFigure = function(cfg)
22148 {
22149     if (cfg.node) {
22150         this.readElement(cfg.node);
22151         this.updateElement(cfg.node);
22152     }
22153     Roo.apply(this, cfg);
22154 }
22155 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
22156  
22157     
22158     // setable values.
22159     image_src: '',
22160     
22161     align: 'left',
22162     caption : '',
22163     text_align: 'left',
22164     
22165     width : '46%',
22166     margin: '2%',
22167     
22168     // used by context menu
22169     friendly_name : 'Image with caption',
22170     
22171     context : { // ?? static really
22172         width : {
22173             title: "Width",
22174             width: 40
22175             // ?? number
22176         },
22177         margin : {
22178             title: "Margin",
22179             width: 40
22180             // ?? number
22181         },
22182         align: {
22183             title: "Align",
22184             opts : [[ "left"],[ "right"]],
22185             width : 80
22186             
22187         },
22188         text_align: {
22189             title: "Caption Align",
22190             opts : [ [ "left"],[ "right"],[ "center"]],
22191             width : 80
22192         },
22193         
22194        
22195         image_src : {
22196             title: "Src",
22197             width: 220
22198         }
22199     },
22200     /**
22201      * create a DomHelper friendly object - for use with
22202      * Roo.DomHelper.markup / overwrite / etc..
22203      */
22204     toObject : function()
22205     {
22206         var d = document.createElement('div');
22207         d.innerHTML = this.caption;
22208         
22209         return {
22210             tag: 'figure',
22211             'data-block' : 'Figure',
22212             contenteditable : 'false',
22213             style : {
22214                 display: 'table',
22215                 float :  this.align ,
22216                 width :  this.width,
22217                 margin:  this.margin
22218             },
22219             cn : [
22220                 {
22221                     tag : 'img',
22222                     src : this.image_src,
22223                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
22224                     style: {
22225                         width: '100%'
22226                     }
22227                 },
22228                 {
22229                     tag: 'figcaption',
22230                     contenteditable : true,
22231                     style : {
22232                         'text-align': this.text_align
22233                     },
22234                     html : this.caption
22235                     
22236                 }
22237             ]
22238         };
22239     },
22240     
22241     readElement : function(node)
22242     {
22243         this.image_src = this.getVal(node, 'img', 'src');
22244         this.align = this.getVal(node, 'figure', 'style', 'float');
22245         this.caption = this.getVal(node, 'figcaption', 'html');
22246         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
22247         this.width = this.getVal(node, 'figure', 'style', 'width');
22248         this.margin = this.getVal(node, 'figure', 'style', 'margin');
22249         
22250     } 
22251     
22252   
22253    
22254      
22255     
22256     
22257     
22258     
22259 })
22260
22261 //<script type="text/javascript">
22262
22263 /*
22264  * Based  Ext JS Library 1.1.1
22265  * Copyright(c) 2006-2007, Ext JS, LLC.
22266  * LGPL
22267  *
22268  */
22269  
22270 /**
22271  * @class Roo.HtmlEditorCore
22272  * @extends Roo.Component
22273  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22274  *
22275  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22276  */
22277
22278 Roo.HtmlEditorCore = function(config){
22279     
22280     
22281     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22282     
22283     
22284     this.addEvents({
22285         /**
22286          * @event initialize
22287          * Fires when the editor is fully initialized (including the iframe)
22288          * @param {Roo.HtmlEditorCore} this
22289          */
22290         initialize: true,
22291         /**
22292          * @event activate
22293          * Fires when the editor is first receives the focus. Any insertion must wait
22294          * until after this event.
22295          * @param {Roo.HtmlEditorCore} this
22296          */
22297         activate: true,
22298          /**
22299          * @event beforesync
22300          * Fires before the textarea is updated with content from the editor iframe. Return false
22301          * to cancel the sync.
22302          * @param {Roo.HtmlEditorCore} this
22303          * @param {String} html
22304          */
22305         beforesync: true,
22306          /**
22307          * @event beforepush
22308          * Fires before the iframe editor is updated with content from the textarea. Return false
22309          * to cancel the push.
22310          * @param {Roo.HtmlEditorCore} this
22311          * @param {String} html
22312          */
22313         beforepush: true,
22314          /**
22315          * @event sync
22316          * Fires when the textarea is updated with content from the editor iframe.
22317          * @param {Roo.HtmlEditorCore} this
22318          * @param {String} html
22319          */
22320         sync: true,
22321          /**
22322          * @event push
22323          * Fires when the iframe editor is updated with content from the textarea.
22324          * @param {Roo.HtmlEditorCore} this
22325          * @param {String} html
22326          */
22327         push: true,
22328         
22329         /**
22330          * @event editorevent
22331          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22332          * @param {Roo.HtmlEditorCore} this
22333          */
22334         editorevent: true
22335         
22336     });
22337     
22338     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22339     
22340     // defaults : white / black...
22341     this.applyBlacklists();
22342     
22343     
22344     
22345 };
22346
22347
22348 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22349
22350
22351      /**
22352      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22353      */
22354     
22355     owner : false,
22356     
22357      /**
22358      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22359      *                        Roo.resizable.
22360      */
22361     resizable : false,
22362      /**
22363      * @cfg {Number} height (in pixels)
22364      */   
22365     height: 300,
22366    /**
22367      * @cfg {Number} width (in pixels)
22368      */   
22369     width: 500,
22370     
22371     /**
22372      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22373      * 
22374      */
22375     stylesheets: false,
22376     
22377     /**
22378      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22379      */
22380     allowComments: false,
22381     // id of frame..
22382     frameId: false,
22383     
22384     // private properties
22385     validationEvent : false,
22386     deferHeight: true,
22387     initialized : false,
22388     activated : false,
22389     sourceEditMode : false,
22390     onFocus : Roo.emptyFn,
22391     iframePad:3,
22392     hideMode:'offsets',
22393     
22394     clearUp: true,
22395     
22396     // blacklist + whitelisted elements..
22397     black: false,
22398     white: false,
22399      
22400     bodyCls : '',
22401
22402     /**
22403      * Protected method that will not generally be called directly. It
22404      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22405      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22406      */
22407     getDocMarkup : function(){
22408         // body styles..
22409         var st = '';
22410         
22411         // inherit styels from page...?? 
22412         if (this.stylesheets === false) {
22413             
22414             Roo.get(document.head).select('style').each(function(node) {
22415                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22416             });
22417             
22418             Roo.get(document.head).select('link').each(function(node) { 
22419                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22420             });
22421             
22422         } else if (!this.stylesheets.length) {
22423                 // simple..
22424                 st = '<style type="text/css">' +
22425                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22426                    '</style>';
22427         } else {
22428             for (var i in this.stylesheets) {
22429                 if (typeof(this.stylesheets[i]) != 'string') {
22430                     continue;
22431                 }
22432                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
22433             }
22434             
22435         }
22436         
22437         st +=  '<style type="text/css">' +
22438             'IMG { cursor: pointer } ' +
22439         '</style>';
22440
22441         var cls = 'roo-htmleditor-body';
22442         
22443         if(this.bodyCls.length){
22444             cls += ' ' + this.bodyCls;
22445         }
22446         
22447         return '<html><head>' + st  +
22448             //<style type="text/css">' +
22449             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22450             //'</style>' +
22451             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
22452     },
22453
22454     // private
22455     onRender : function(ct, position)
22456     {
22457         var _t = this;
22458         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22459         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22460         
22461         
22462         this.el.dom.style.border = '0 none';
22463         this.el.dom.setAttribute('tabIndex', -1);
22464         this.el.addClass('x-hidden hide');
22465         
22466         
22467         
22468         if(Roo.isIE){ // fix IE 1px bogus margin
22469             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22470         }
22471        
22472         
22473         this.frameId = Roo.id();
22474         
22475          
22476         
22477         var iframe = this.owner.wrap.createChild({
22478             tag: 'iframe',
22479             cls: 'form-control', // bootstrap..
22480             id: this.frameId,
22481             name: this.frameId,
22482             frameBorder : 'no',
22483             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22484         }, this.el
22485         );
22486         
22487         
22488         this.iframe = iframe.dom;
22489
22490         this.assignDocWin();
22491         
22492         this.doc.designMode = 'on';
22493        
22494         this.doc.open();
22495         this.doc.write(this.getDocMarkup());
22496         this.doc.close();
22497
22498         
22499         var task = { // must defer to wait for browser to be ready
22500             run : function(){
22501                 //console.log("run task?" + this.doc.readyState);
22502                 this.assignDocWin();
22503                 if(this.doc.body || this.doc.readyState == 'complete'){
22504                     try {
22505                         this.doc.designMode="on";
22506                     } catch (e) {
22507                         return;
22508                     }
22509                     Roo.TaskMgr.stop(task);
22510                     this.initEditor.defer(10, this);
22511                 }
22512             },
22513             interval : 10,
22514             duration: 10000,
22515             scope: this
22516         };
22517         Roo.TaskMgr.start(task);
22518
22519     },
22520
22521     // private
22522     onResize : function(w, h)
22523     {
22524          Roo.log('resize: ' +w + ',' + h );
22525         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22526         if(!this.iframe){
22527             return;
22528         }
22529         if(typeof w == 'number'){
22530             
22531             this.iframe.style.width = w + 'px';
22532         }
22533         if(typeof h == 'number'){
22534             
22535             this.iframe.style.height = h + 'px';
22536             if(this.doc){
22537                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22538             }
22539         }
22540         
22541     },
22542
22543     /**
22544      * Toggles the editor between standard and source edit mode.
22545      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22546      */
22547     toggleSourceEdit : function(sourceEditMode){
22548         
22549         this.sourceEditMode = sourceEditMode === true;
22550         
22551         if(this.sourceEditMode){
22552  
22553             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
22554             
22555         }else{
22556             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
22557             //this.iframe.className = '';
22558             this.deferFocus();
22559         }
22560         //this.setSize(this.owner.wrap.getSize());
22561         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22562     },
22563
22564     
22565   
22566
22567     /**
22568      * Protected method that will not generally be called directly. If you need/want
22569      * custom HTML cleanup, this is the method you should override.
22570      * @param {String} html The HTML to be cleaned
22571      * return {String} The cleaned HTML
22572      */
22573     cleanHtml : function(html){
22574         html = String(html);
22575         if(html.length > 5){
22576             if(Roo.isSafari){ // strip safari nonsense
22577                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22578             }
22579         }
22580         if(html == '&nbsp;'){
22581             html = '';
22582         }
22583         return html;
22584     },
22585
22586     /**
22587      * HTML Editor -> Textarea
22588      * Protected method that will not generally be called directly. Syncs the contents
22589      * of the editor iframe with the textarea.
22590      */
22591     syncValue : function()
22592     {
22593         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
22594         if(this.initialized){
22595             var bd = (this.doc.body || this.doc.documentElement);
22596             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22597             
22598             // not sure if this is really the place for this
22599             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
22600             // this has to update attributes that get duped.. like alt and caption..
22601             
22602             
22603             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
22604             //     Roo.htmleditor.Block.factory(e);
22605             //},this);
22606             
22607             
22608             var div = document.createElement('div');
22609             div.innerHTML = bd.innerHTML;
22610             // remove content editable. (blocks)
22611             
22612            
22613             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
22614             //?? tidy?
22615             var html = div.innerHTML;
22616             if(Roo.isSafari){
22617                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22618                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22619                 if(m && m[1]){
22620                     html = '<div style="'+m[0]+'">' + html + '</div>';
22621                 }
22622             }
22623             html = this.cleanHtml(html);
22624             // fix up the special chars.. normaly like back quotes in word...
22625             // however we do not want to do this with chinese..
22626             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22627                 
22628                 var cc = match.charCodeAt();
22629
22630                 // Get the character value, handling surrogate pairs
22631                 if (match.length == 2) {
22632                     // It's a surrogate pair, calculate the Unicode code point
22633                     var high = match.charCodeAt(0) - 0xD800;
22634                     var low  = match.charCodeAt(1) - 0xDC00;
22635                     cc = (high * 0x400) + low + 0x10000;
22636                 }  else if (
22637                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22638                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22639                     (cc >= 0xf900 && cc < 0xfb00 )
22640                 ) {
22641                         return match;
22642                 }  
22643          
22644                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22645                 return "&#" + cc + ";";
22646                 
22647                 
22648             });
22649             
22650             
22651              
22652             if(this.owner.fireEvent('beforesync', this, html) !== false){
22653                 this.el.dom.value = html;
22654                 this.owner.fireEvent('sync', this, html);
22655             }
22656         }
22657     },
22658
22659     /**
22660      * TEXTAREA -> EDITABLE
22661      * Protected method that will not generally be called directly. Pushes the value of the textarea
22662      * into the iframe editor.
22663      */
22664     pushValue : function()
22665     {
22666         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
22667         if(this.initialized){
22668             var v = this.el.dom.value.trim();
22669             
22670             
22671             if(this.owner.fireEvent('beforepush', this, v) !== false){
22672                 var d = (this.doc.body || this.doc.documentElement);
22673                 d.innerHTML = v;
22674                  
22675                 this.el.dom.value = d.innerHTML;
22676                 this.owner.fireEvent('push', this, v);
22677             }
22678             
22679             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
22680                 
22681                 Roo.htmleditor.Block.factory(e);
22682                 
22683             },this);
22684             var lc = this.doc.body.lastChild;
22685             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
22686                 // add an extra line at the end.
22687                 this.doc.body.appendChild(this.doc.createElement('br'));
22688             }
22689             
22690             
22691         }
22692     },
22693
22694     // private
22695     deferFocus : function(){
22696         this.focus.defer(10, this);
22697     },
22698
22699     // doc'ed in Field
22700     focus : function(){
22701         if(this.win && !this.sourceEditMode){
22702             this.win.focus();
22703         }else{
22704             this.el.focus();
22705         }
22706     },
22707     
22708     assignDocWin: function()
22709     {
22710         var iframe = this.iframe;
22711         
22712          if(Roo.isIE){
22713             this.doc = iframe.contentWindow.document;
22714             this.win = iframe.contentWindow;
22715         } else {
22716 //            if (!Roo.get(this.frameId)) {
22717 //                return;
22718 //            }
22719 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22720 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22721             
22722             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22723                 return;
22724             }
22725             
22726             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22727             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22728         }
22729     },
22730     
22731     // private
22732     initEditor : function(){
22733         //console.log("INIT EDITOR");
22734         this.assignDocWin();
22735         
22736         
22737         
22738         this.doc.designMode="on";
22739         this.doc.open();
22740         this.doc.write(this.getDocMarkup());
22741         this.doc.close();
22742         
22743         var dbody = (this.doc.body || this.doc.documentElement);
22744         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22745         // this copies styles from the containing element into thsi one..
22746         // not sure why we need all of this..
22747         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22748         
22749         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22750         //ss['background-attachment'] = 'fixed'; // w3c
22751         dbody.bgProperties = 'fixed'; // ie
22752         //Roo.DomHelper.applyStyles(dbody, ss);
22753         Roo.EventManager.on(this.doc, {
22754             //'mousedown': this.onEditorEvent,
22755             'mouseup': this.onEditorEvent,
22756             'dblclick': this.onEditorEvent,
22757             'click': this.onEditorEvent,
22758             'keyup': this.onEditorEvent,
22759             
22760             buffer:100,
22761             scope: this
22762         });
22763         Roo.EventManager.on(this.doc, {
22764             'paste': this.onPasteEvent,
22765             scope : this
22766         });
22767         if(Roo.isGecko){
22768             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22769         }
22770         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22771             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22772         }
22773         this.initialized = true;
22774
22775         
22776         // initialize special key events - enter
22777         new Roo.htmleditor.KeyEnter({core : this});
22778         
22779          
22780         
22781         this.owner.fireEvent('initialize', this);
22782         this.pushValue();
22783     },
22784     
22785     onPasteEvent : function(e,v)
22786     {
22787         // I think we better assume paste is going to be a dirty load of rubish from word..
22788         
22789         // even pasting into a 'email version' of this widget will have to clean up that mess.
22790         var cd = (e.browserEvent.clipboardData || window.clipboardData);
22791         
22792         // check what type of paste - if it's an image, then handle it differently.
22793         if (cd.files.length > 0) {
22794             // pasting images?
22795             var urlAPI = (window.createObjectURL && window) || 
22796                 (window.URL && URL.revokeObjectURL && URL) || 
22797                 (window.webkitURL && webkitURL);
22798     
22799             var url = urlAPI.createObjectURL( cd.files[0]);
22800             this.insertAtCursor('<img src=" + url + ">');
22801             return false;
22802         }
22803         
22804         var html = cd.getData('text/html'); // clipboard event
22805         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
22806         var images = parser.doc.getElementsByType('pict');
22807         Roo.log(images);
22808         //Roo.log(imgs);
22809         // fixme..
22810         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
22811                        .map(function(g) { return g.toDataURL(); });
22812         
22813         
22814         html = this.cleanWordChars(html);
22815         
22816         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
22817         
22818         if (images.length > 0) {
22819             Roo.each(d.getElementsByTagName('img'), function(img, i) {
22820                 img.setAttribute('src', images[i]);
22821             });
22822         }
22823         
22824       
22825         new Roo.htmleditor.FilterStyleToTag({ node : d });
22826         new Roo.htmleditor.FilterAttributes({
22827             node : d,
22828             attrib_white : ['href', 'src', 'name', 'align'],
22829             attrib_clean : ['href', 'src' ] 
22830         });
22831         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
22832         // should be fonts..
22833         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
22834         new Roo.htmleditor.FilterParagraph({ node : d });
22835         new Roo.htmleditor.FilterSpan({ node : d });
22836         new Roo.htmleditor.FilterLongBr({ node : d });
22837         
22838         
22839         
22840         this.insertAtCursor(d.innerHTML);
22841         
22842         e.preventDefault();
22843         return false;
22844         // default behaveiour should be our local cleanup paste? (optional?)
22845         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
22846         //this.owner.fireEvent('paste', e, v);
22847     },
22848     // private
22849     onDestroy : function(){
22850         
22851         
22852         
22853         if(this.rendered){
22854             
22855             //for (var i =0; i < this.toolbars.length;i++) {
22856             //    // fixme - ask toolbars for heights?
22857             //    this.toolbars[i].onDestroy();
22858            // }
22859             
22860             //this.wrap.dom.innerHTML = '';
22861             //this.wrap.remove();
22862         }
22863     },
22864
22865     // private
22866     onFirstFocus : function(){
22867         
22868         this.assignDocWin();
22869         
22870         
22871         this.activated = true;
22872          
22873     
22874         if(Roo.isGecko){ // prevent silly gecko errors
22875             this.win.focus();
22876             var s = this.win.getSelection();
22877             if(!s.focusNode || s.focusNode.nodeType != 3){
22878                 var r = s.getRangeAt(0);
22879                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22880                 r.collapse(true);
22881                 this.deferFocus();
22882             }
22883             try{
22884                 this.execCmd('useCSS', true);
22885                 this.execCmd('styleWithCSS', false);
22886             }catch(e){}
22887         }
22888         this.owner.fireEvent('activate', this);
22889     },
22890
22891     // private
22892     adjustFont: function(btn){
22893         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22894         //if(Roo.isSafari){ // safari
22895         //    adjust *= 2;
22896        // }
22897         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22898         if(Roo.isSafari){ // safari
22899             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22900             v =  (v < 10) ? 10 : v;
22901             v =  (v > 48) ? 48 : v;
22902             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22903             
22904         }
22905         
22906         
22907         v = Math.max(1, v+adjust);
22908         
22909         this.execCmd('FontSize', v  );
22910     },
22911
22912     onEditorEvent : function(e)
22913     {
22914         this.owner.fireEvent('editorevent', this, e);
22915       //  this.updateToolbar();
22916         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22917     },
22918
22919     insertTag : function(tg)
22920     {
22921         // could be a bit smarter... -> wrap the current selected tRoo..
22922         if (tg.toLowerCase() == 'span' ||
22923             tg.toLowerCase() == 'code' ||
22924             tg.toLowerCase() == 'sup' ||
22925             tg.toLowerCase() == 'sub' 
22926             ) {
22927             
22928             range = this.createRange(this.getSelection());
22929             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22930             wrappingNode.appendChild(range.extractContents());
22931             range.insertNode(wrappingNode);
22932
22933             return;
22934             
22935             
22936             
22937         }
22938         this.execCmd("formatblock",   tg);
22939         
22940     },
22941     
22942     insertText : function(txt)
22943     {
22944         
22945         
22946         var range = this.createRange();
22947         range.deleteContents();
22948                //alert(Sender.getAttribute('label'));
22949                
22950         range.insertNode(this.doc.createTextNode(txt));
22951     } ,
22952     
22953      
22954
22955     /**
22956      * Executes a Midas editor command on the editor document and performs necessary focus and
22957      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22958      * @param {String} cmd The Midas command
22959      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22960      */
22961     relayCmd : function(cmd, value){
22962         this.win.focus();
22963         this.execCmd(cmd, value);
22964         this.owner.fireEvent('editorevent', this);
22965         //this.updateToolbar();
22966         this.owner.deferFocus();
22967     },
22968
22969     /**
22970      * Executes a Midas editor command directly on the editor document.
22971      * For visual commands, you should use {@link #relayCmd} instead.
22972      * <b>This should only be called after the editor is initialized.</b>
22973      * @param {String} cmd The Midas command
22974      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22975      */
22976     execCmd : function(cmd, value){
22977         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22978         this.syncValue();
22979     },
22980  
22981  
22982    
22983     /**
22984      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22985      * to insert tRoo.
22986      * @param {String} text | dom node.. 
22987      */
22988     insertAtCursor : function(text)
22989     {
22990         
22991         if(!this.activated){
22992             return;
22993         }
22994          
22995         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22996             this.win.focus();
22997             
22998             
22999             // from jquery ui (MIT licenced)
23000             var range, node;
23001             var win = this.win;
23002             
23003             if (win.getSelection && win.getSelection().getRangeAt) {
23004                 
23005                 // delete the existing?
23006                 
23007                 this.createRange(this.getSelection()).deleteContents();
23008                 range = win.getSelection().getRangeAt(0);
23009                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23010                 range.insertNode(node);
23011             } else if (win.document.selection && win.document.selection.createRange) {
23012                 // no firefox support
23013                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23014                 win.document.selection.createRange().pasteHTML(txt);
23015             } else {
23016                 // no firefox support
23017                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23018                 this.execCmd('InsertHTML', txt);
23019             } 
23020             
23021             this.syncValue();
23022             
23023             this.deferFocus();
23024         }
23025     },
23026  // private
23027     mozKeyPress : function(e){
23028         if(e.ctrlKey){
23029             var c = e.getCharCode(), cmd;
23030           
23031             if(c > 0){
23032                 c = String.fromCharCode(c).toLowerCase();
23033                 switch(c){
23034                     case 'b':
23035                         cmd = 'bold';
23036                         break;
23037                     case 'i':
23038                         cmd = 'italic';
23039                         break;
23040                     
23041                     case 'u':
23042                         cmd = 'underline';
23043                         break;
23044                     
23045                     //case 'v':
23046                       //  this.cleanUpPaste.defer(100, this);
23047                       //  return;
23048                         
23049                 }
23050                 if(cmd){
23051                     this.win.focus();
23052                     this.execCmd(cmd);
23053                     this.deferFocus();
23054                     e.preventDefault();
23055                 }
23056                 
23057             }
23058         }
23059     },
23060
23061     // private
23062     fixKeys : function(){ // load time branching for fastest keydown performance
23063         if(Roo.isIE){
23064             return function(e){
23065                 var k = e.getKey(), r;
23066                 if(k == e.TAB){
23067                     e.stopEvent();
23068                     r = this.doc.selection.createRange();
23069                     if(r){
23070                         r.collapse(true);
23071                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23072                         this.deferFocus();
23073                     }
23074                     return;
23075                 }
23076                 
23077                 if(k == e.ENTER){
23078                     r = this.doc.selection.createRange();
23079                     if(r){
23080                         var target = r.parentElement();
23081                         if(!target || target.tagName.toLowerCase() != 'li'){
23082                             e.stopEvent();
23083                             r.pasteHTML('<br/>');
23084                             r.collapse(false);
23085                             r.select();
23086                         }
23087                     }
23088                 }
23089                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23090                 //    this.cleanUpPaste.defer(100, this);
23091                 //    return;
23092                 //}
23093                 
23094                 
23095             };
23096         }else if(Roo.isOpera){
23097             return function(e){
23098                 var k = e.getKey();
23099                 if(k == e.TAB){
23100                     e.stopEvent();
23101                     this.win.focus();
23102                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23103                     this.deferFocus();
23104                 }
23105                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23106                 //    this.cleanUpPaste.defer(100, this);
23107                  //   return;
23108                 //}
23109                 
23110             };
23111         }else if(Roo.isSafari){
23112             return function(e){
23113                 var k = e.getKey();
23114                 
23115                 if(k == e.TAB){
23116                     e.stopEvent();
23117                     this.execCmd('InsertText','\t');
23118                     this.deferFocus();
23119                     return;
23120                 }
23121                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23122                  //   this.cleanUpPaste.defer(100, this);
23123                  //   return;
23124                // }
23125                 
23126              };
23127         }
23128     }(),
23129     
23130     getAllAncestors: function()
23131     {
23132         var p = this.getSelectedNode();
23133         var a = [];
23134         if (!p) {
23135             a.push(p); // push blank onto stack..
23136             p = this.getParentElement();
23137         }
23138         
23139         
23140         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23141             a.push(p);
23142             p = p.parentNode;
23143         }
23144         a.push(this.doc.body);
23145         return a;
23146     },
23147     lastSel : false,
23148     lastSelNode : false,
23149     
23150     
23151     getSelection : function() 
23152     {
23153         this.assignDocWin();
23154         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23155     },
23156     /**
23157      * Select a dom node
23158      * @param {DomElement} node the node to select
23159      */
23160     selectNode : function(node)
23161     {
23162         
23163             var nodeRange = node.ownerDocument.createRange();
23164             try {
23165                 nodeRange.selectNode(node);
23166             } catch (e) {
23167                 nodeRange.selectNodeContents(node);
23168             }
23169             //nodeRange.collapse(true);
23170             var s = this.win.getSelection();
23171             s.removeAllRanges();
23172             s.addRange(nodeRange);
23173     },
23174     
23175     getSelectedNode: function() 
23176     {
23177         // this may only work on Gecko!!!
23178         
23179         // should we cache this!!!!
23180         
23181         
23182         
23183          
23184         var range = this.createRange(this.getSelection()).cloneRange();
23185         
23186         if (Roo.isIE) {
23187             var parent = range.parentElement();
23188             while (true) {
23189                 var testRange = range.duplicate();
23190                 testRange.moveToElementText(parent);
23191                 if (testRange.inRange(range)) {
23192                     break;
23193                 }
23194                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23195                     break;
23196                 }
23197                 parent = parent.parentElement;
23198             }
23199             return parent;
23200         }
23201         
23202         // is ancestor a text element.
23203         var ac =  range.commonAncestorContainer;
23204         if (ac.nodeType == 3) {
23205             ac = ac.parentNode;
23206         }
23207         
23208         var ar = ac.childNodes;
23209          
23210         var nodes = [];
23211         var other_nodes = [];
23212         var has_other_nodes = false;
23213         for (var i=0;i<ar.length;i++) {
23214             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23215                 continue;
23216             }
23217             // fullly contained node.
23218             
23219             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23220                 nodes.push(ar[i]);
23221                 continue;
23222             }
23223             
23224             // probably selected..
23225             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23226                 other_nodes.push(ar[i]);
23227                 continue;
23228             }
23229             // outer..
23230             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23231                 continue;
23232             }
23233             
23234             
23235             has_other_nodes = true;
23236         }
23237         if (!nodes.length && other_nodes.length) {
23238             nodes= other_nodes;
23239         }
23240         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23241             return false;
23242         }
23243         
23244         return nodes[0];
23245     },
23246     createRange: function(sel)
23247     {
23248         // this has strange effects when using with 
23249         // top toolbar - not sure if it's a great idea.
23250         //this.editor.contentWindow.focus();
23251         if (typeof sel != "undefined") {
23252             try {
23253                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23254             } catch(e) {
23255                 return this.doc.createRange();
23256             }
23257         } else {
23258             return this.doc.createRange();
23259         }
23260     },
23261     getParentElement: function()
23262     {
23263         
23264         this.assignDocWin();
23265         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23266         
23267         var range = this.createRange(sel);
23268          
23269         try {
23270             var p = range.commonAncestorContainer;
23271             while (p.nodeType == 3) { // text node
23272                 p = p.parentNode;
23273             }
23274             return p;
23275         } catch (e) {
23276             return null;
23277         }
23278     
23279     },
23280     /***
23281      *
23282      * Range intersection.. the hard stuff...
23283      *  '-1' = before
23284      *  '0' = hits..
23285      *  '1' = after.
23286      *         [ -- selected range --- ]
23287      *   [fail]                        [fail]
23288      *
23289      *    basically..
23290      *      if end is before start or  hits it. fail.
23291      *      if start is after end or hits it fail.
23292      *
23293      *   if either hits (but other is outside. - then it's not 
23294      *   
23295      *    
23296      **/
23297     
23298     
23299     // @see http://www.thismuchiknow.co.uk/?p=64.
23300     rangeIntersectsNode : function(range, node)
23301     {
23302         var nodeRange = node.ownerDocument.createRange();
23303         try {
23304             nodeRange.selectNode(node);
23305         } catch (e) {
23306             nodeRange.selectNodeContents(node);
23307         }
23308     
23309         var rangeStartRange = range.cloneRange();
23310         rangeStartRange.collapse(true);
23311     
23312         var rangeEndRange = range.cloneRange();
23313         rangeEndRange.collapse(false);
23314     
23315         var nodeStartRange = nodeRange.cloneRange();
23316         nodeStartRange.collapse(true);
23317     
23318         var nodeEndRange = nodeRange.cloneRange();
23319         nodeEndRange.collapse(false);
23320     
23321         return rangeStartRange.compareBoundaryPoints(
23322                  Range.START_TO_START, nodeEndRange) == -1 &&
23323                rangeEndRange.compareBoundaryPoints(
23324                  Range.START_TO_START, nodeStartRange) == 1;
23325         
23326          
23327     },
23328     rangeCompareNode : function(range, node)
23329     {
23330         var nodeRange = node.ownerDocument.createRange();
23331         try {
23332             nodeRange.selectNode(node);
23333         } catch (e) {
23334             nodeRange.selectNodeContents(node);
23335         }
23336         
23337         
23338         range.collapse(true);
23339     
23340         nodeRange.collapse(true);
23341      
23342         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23343         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23344          
23345         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23346         
23347         var nodeIsBefore   =  ss == 1;
23348         var nodeIsAfter    = ee == -1;
23349         
23350         if (nodeIsBefore && nodeIsAfter) {
23351             return 0; // outer
23352         }
23353         if (!nodeIsBefore && nodeIsAfter) {
23354             return 1; //right trailed.
23355         }
23356         
23357         if (nodeIsBefore && !nodeIsAfter) {
23358             return 2;  // left trailed.
23359         }
23360         // fully contined.
23361         return 3;
23362     },
23363  
23364     cleanWordChars : function(input) {// change the chars to hex code
23365         
23366        var swapCodes  = [ 
23367             [    8211, "&#8211;" ], 
23368             [    8212, "&#8212;" ], 
23369             [    8216,  "'" ],  
23370             [    8217, "'" ],  
23371             [    8220, '"' ],  
23372             [    8221, '"' ],  
23373             [    8226, "*" ],  
23374             [    8230, "..." ]
23375         ]; 
23376         var output = input;
23377         Roo.each(swapCodes, function(sw) { 
23378             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23379             
23380             output = output.replace(swapper, sw[1]);
23381         });
23382         
23383         return output;
23384     },
23385     
23386      
23387     
23388         
23389     
23390     cleanUpChild : function (node)
23391     {
23392         
23393         new Roo.htmleditor.FilterComment({node : node});
23394         new Roo.htmleditor.FilterAttributes({
23395                 node : node,
23396                 attrib_black : this.ablack,
23397                 attrib_clean : this.aclean,
23398                 style_white : this.cwhite,
23399                 style_black : this.cblack
23400         });
23401         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
23402         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
23403          
23404         
23405     },
23406     
23407     /**
23408      * Clean up MS wordisms...
23409      * @deprecated - use filter directly
23410      */
23411     cleanWord : function(node)
23412     {
23413         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
23414         
23415     },
23416    
23417     
23418     /**
23419
23420      * @deprecated - use filters
23421      */
23422     cleanTableWidths : function(node)
23423     {
23424         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
23425         
23426  
23427     },
23428     
23429      
23430         
23431     applyBlacklists : function()
23432     {
23433         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23434         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23435         
23436         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
23437         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
23438         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
23439         
23440         this.white = [];
23441         this.black = [];
23442         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23443             if (b.indexOf(tag) > -1) {
23444                 return;
23445             }
23446             this.white.push(tag);
23447             
23448         }, this);
23449         
23450         Roo.each(w, function(tag) {
23451             if (b.indexOf(tag) > -1) {
23452                 return;
23453             }
23454             if (this.white.indexOf(tag) > -1) {
23455                 return;
23456             }
23457             this.white.push(tag);
23458             
23459         }, this);
23460         
23461         
23462         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23463             if (w.indexOf(tag) > -1) {
23464                 return;
23465             }
23466             this.black.push(tag);
23467             
23468         }, this);
23469         
23470         Roo.each(b, function(tag) {
23471             if (w.indexOf(tag) > -1) {
23472                 return;
23473             }
23474             if (this.black.indexOf(tag) > -1) {
23475                 return;
23476             }
23477             this.black.push(tag);
23478             
23479         }, this);
23480         
23481         
23482         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23483         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23484         
23485         this.cwhite = [];
23486         this.cblack = [];
23487         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23488             if (b.indexOf(tag) > -1) {
23489                 return;
23490             }
23491             this.cwhite.push(tag);
23492             
23493         }, this);
23494         
23495         Roo.each(w, function(tag) {
23496             if (b.indexOf(tag) > -1) {
23497                 return;
23498             }
23499             if (this.cwhite.indexOf(tag) > -1) {
23500                 return;
23501             }
23502             this.cwhite.push(tag);
23503             
23504         }, this);
23505         
23506         
23507         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23508             if (w.indexOf(tag) > -1) {
23509                 return;
23510             }
23511             this.cblack.push(tag);
23512             
23513         }, this);
23514         
23515         Roo.each(b, function(tag) {
23516             if (w.indexOf(tag) > -1) {
23517                 return;
23518             }
23519             if (this.cblack.indexOf(tag) > -1) {
23520                 return;
23521             }
23522             this.cblack.push(tag);
23523             
23524         }, this);
23525     },
23526     
23527     setStylesheets : function(stylesheets)
23528     {
23529         if(typeof(stylesheets) == 'string'){
23530             Roo.get(this.iframe.contentDocument.head).createChild({
23531                 tag : 'link',
23532                 rel : 'stylesheet',
23533                 type : 'text/css',
23534                 href : stylesheets
23535             });
23536             
23537             return;
23538         }
23539         var _this = this;
23540      
23541         Roo.each(stylesheets, function(s) {
23542             if(!s.length){
23543                 return;
23544             }
23545             
23546             Roo.get(_this.iframe.contentDocument.head).createChild({
23547                 tag : 'link',
23548                 rel : 'stylesheet',
23549                 type : 'text/css',
23550                 href : s
23551             });
23552         });
23553
23554         
23555     },
23556     
23557     removeStylesheets : function()
23558     {
23559         var _this = this;
23560         
23561         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23562             s.remove();
23563         });
23564     },
23565     
23566     setStyle : function(style)
23567     {
23568         Roo.get(this.iframe.contentDocument.head).createChild({
23569             tag : 'style',
23570             type : 'text/css',
23571             html : style
23572         });
23573
23574         return;
23575     }
23576     
23577     // hide stuff that is not compatible
23578     /**
23579      * @event blur
23580      * @hide
23581      */
23582     /**
23583      * @event change
23584      * @hide
23585      */
23586     /**
23587      * @event focus
23588      * @hide
23589      */
23590     /**
23591      * @event specialkey
23592      * @hide
23593      */
23594     /**
23595      * @cfg {String} fieldClass @hide
23596      */
23597     /**
23598      * @cfg {String} focusClass @hide
23599      */
23600     /**
23601      * @cfg {String} autoCreate @hide
23602      */
23603     /**
23604      * @cfg {String} inputType @hide
23605      */
23606     /**
23607      * @cfg {String} invalidClass @hide
23608      */
23609     /**
23610      * @cfg {String} invalidText @hide
23611      */
23612     /**
23613      * @cfg {String} msgFx @hide
23614      */
23615     /**
23616      * @cfg {String} validateOnBlur @hide
23617      */
23618 });
23619
23620 Roo.HtmlEditorCore.white = [
23621         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
23622         
23623        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
23624        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
23625        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
23626        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
23627        'TABLE',   'UL',         'XMP', 
23628        
23629        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
23630       'THEAD',   'TR', 
23631      
23632       'DIR', 'MENU', 'OL', 'UL', 'DL',
23633        
23634       'EMBED',  'OBJECT'
23635 ];
23636
23637
23638 Roo.HtmlEditorCore.black = [
23639     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23640         'APPLET', // 
23641         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
23642         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
23643         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
23644         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
23645         //'FONT' // CLEAN LATER..
23646         'COLGROUP', 'COL'  // messy tables.
23647         
23648 ];
23649 Roo.HtmlEditorCore.clean = [ // ?? needed???
23650      'SCRIPT', 'STYLE', 'TITLE', 'XML'
23651 ];
23652 Roo.HtmlEditorCore.tag_remove = [
23653     'FONT', 'TBODY'  
23654 ];
23655 // attributes..
23656
23657 Roo.HtmlEditorCore.ablack = [
23658     'on'
23659 ];
23660     
23661 Roo.HtmlEditorCore.aclean = [ 
23662     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23663 ];
23664
23665 // protocols..
23666 Roo.HtmlEditorCore.pwhite= [
23667         'http',  'https',  'mailto'
23668 ];
23669
23670 // white listed style attributes.
23671 Roo.HtmlEditorCore.cwhite= [
23672       //  'text-align', /// default is to allow most things..
23673       
23674          
23675 //        'font-size'//??
23676 ];
23677
23678 // black listed style attributes.
23679 Roo.HtmlEditorCore.cblack= [
23680       //  'font-size' -- this can be set by the project 
23681 ];
23682
23683
23684
23685
23686     //<script type="text/javascript">
23687
23688 /*
23689  * Ext JS Library 1.1.1
23690  * Copyright(c) 2006-2007, Ext JS, LLC.
23691  * Licence LGPL
23692  * 
23693  */
23694  
23695  
23696 Roo.form.HtmlEditor = function(config){
23697     
23698     
23699     
23700     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
23701     
23702     if (!this.toolbars) {
23703         this.toolbars = [];
23704     }
23705     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23706     
23707     
23708 };
23709
23710 /**
23711  * @class Roo.form.HtmlEditor
23712  * @extends Roo.form.Field
23713  * Provides a lightweight HTML Editor component.
23714  *
23715  * This has been tested on Fireforx / Chrome.. IE may not be so great..
23716  * 
23717  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23718  * supported by this editor.</b><br/><br/>
23719  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23720  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23721  */
23722 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
23723     /**
23724      * @cfg {Boolean} clearUp
23725      */
23726     clearUp : true,
23727       /**
23728      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23729      */
23730     toolbars : false,
23731    
23732      /**
23733      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23734      *                        Roo.resizable.
23735      */
23736     resizable : false,
23737      /**
23738      * @cfg {Number} height (in pixels)
23739      */   
23740     height: 300,
23741    /**
23742      * @cfg {Number} width (in pixels)
23743      */   
23744     width: 500,
23745     
23746     /**
23747      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
23748      * 
23749      */
23750     stylesheets: false,
23751     
23752     
23753      /**
23754      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
23755      * 
23756      */
23757     cblack: false,
23758     /**
23759      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
23760      * 
23761      */
23762     cwhite: false,
23763     
23764      /**
23765      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
23766      * 
23767      */
23768     black: false,
23769     /**
23770      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
23771      * 
23772      */
23773     white: false,
23774     /**
23775      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
23776      */
23777     allowComments: false,
23778     /**
23779      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
23780      */
23781     
23782     
23783      bodyCls : '',
23784     
23785     // id of frame..
23786     frameId: false,
23787     
23788     // private properties
23789     validationEvent : false,
23790     deferHeight: true,
23791     initialized : false,
23792     activated : false,
23793     
23794     onFocus : Roo.emptyFn,
23795     iframePad:3,
23796     hideMode:'offsets',
23797     
23798     actionMode : 'container', // defaults to hiding it...
23799     
23800     defaultAutoCreate : { // modified by initCompnoent..
23801         tag: "textarea",
23802         style:"width:500px;height:300px;",
23803         autocomplete: "new-password"
23804     },
23805
23806     // private
23807     initComponent : function(){
23808         this.addEvents({
23809             /**
23810              * @event initialize
23811              * Fires when the editor is fully initialized (including the iframe)
23812              * @param {HtmlEditor} this
23813              */
23814             initialize: true,
23815             /**
23816              * @event activate
23817              * Fires when the editor is first receives the focus. Any insertion must wait
23818              * until after this event.
23819              * @param {HtmlEditor} this
23820              */
23821             activate: true,
23822              /**
23823              * @event beforesync
23824              * Fires before the textarea is updated with content from the editor iframe. Return false
23825              * to cancel the sync.
23826              * @param {HtmlEditor} this
23827              * @param {String} html
23828              */
23829             beforesync: true,
23830              /**
23831              * @event beforepush
23832              * Fires before the iframe editor is updated with content from the textarea. Return false
23833              * to cancel the push.
23834              * @param {HtmlEditor} this
23835              * @param {String} html
23836              */
23837             beforepush: true,
23838              /**
23839              * @event sync
23840              * Fires when the textarea is updated with content from the editor iframe.
23841              * @param {HtmlEditor} this
23842              * @param {String} html
23843              */
23844             sync: true,
23845              /**
23846              * @event push
23847              * Fires when the iframe editor is updated with content from the textarea.
23848              * @param {HtmlEditor} this
23849              * @param {String} html
23850              */
23851             push: true,
23852              /**
23853              * @event editmodechange
23854              * Fires when the editor switches edit modes
23855              * @param {HtmlEditor} this
23856              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23857              */
23858             editmodechange: true,
23859             /**
23860              * @event editorevent
23861              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23862              * @param {HtmlEditor} this
23863              */
23864             editorevent: true,
23865             /**
23866              * @event firstfocus
23867              * Fires when on first focus - needed by toolbars..
23868              * @param {HtmlEditor} this
23869              */
23870             firstfocus: true,
23871             /**
23872              * @event autosave
23873              * Auto save the htmlEditor value as a file into Events
23874              * @param {HtmlEditor} this
23875              */
23876             autosave: true,
23877             /**
23878              * @event savedpreview
23879              * preview the saved version of htmlEditor
23880              * @param {HtmlEditor} this
23881              */
23882             savedpreview: true,
23883             
23884             /**
23885             * @event stylesheetsclick
23886             * Fires when press the Sytlesheets button
23887             * @param {Roo.HtmlEditorCore} this
23888             */
23889             stylesheetsclick: true,
23890             /**
23891             * @event paste
23892             * Fires when press user pastes into the editor
23893             * @param {Roo.HtmlEditorCore} this
23894             */
23895             paste: true 
23896         });
23897         this.defaultAutoCreate =  {
23898             tag: "textarea",
23899             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
23900             autocomplete: "new-password"
23901         };
23902     },
23903
23904     /**
23905      * Protected method that will not generally be called directly. It
23906      * is called when the editor creates its toolbar. Override this method if you need to
23907      * add custom toolbar buttons.
23908      * @param {HtmlEditor} editor
23909      */
23910     createToolbar : function(editor){
23911         Roo.log("create toolbars");
23912         if (!editor.toolbars || !editor.toolbars.length) {
23913             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23914         }
23915         
23916         for (var i =0 ; i < editor.toolbars.length;i++) {
23917             editor.toolbars[i] = Roo.factory(
23918                     typeof(editor.toolbars[i]) == 'string' ?
23919                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
23920                 Roo.form.HtmlEditor);
23921             editor.toolbars[i].init(editor);
23922         }
23923          
23924         
23925     },
23926
23927      
23928     // private
23929     onRender : function(ct, position)
23930     {
23931         var _t = this;
23932         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23933         
23934         this.wrap = this.el.wrap({
23935             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23936         });
23937         
23938         this.editorcore.onRender(ct, position);
23939          
23940         if (this.resizable) {
23941             this.resizeEl = new Roo.Resizable(this.wrap, {
23942                 pinned : true,
23943                 wrap: true,
23944                 dynamic : true,
23945                 minHeight : this.height,
23946                 height: this.height,
23947                 handles : this.resizable,
23948                 width: this.width,
23949                 listeners : {
23950                     resize : function(r, w, h) {
23951                         _t.onResize(w,h); // -something
23952                     }
23953                 }
23954             });
23955             
23956         }
23957         this.createToolbar(this);
23958        
23959         
23960         if(!this.width){
23961             this.setSize(this.wrap.getSize());
23962         }
23963         if (this.resizeEl) {
23964             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23965             // should trigger onReize..
23966         }
23967         
23968         this.keyNav = new Roo.KeyNav(this.el, {
23969             
23970             "tab" : function(e){
23971                 e.preventDefault();
23972                 
23973                 var value = this.getValue();
23974                 
23975                 var start = this.el.dom.selectionStart;
23976                 var end = this.el.dom.selectionEnd;
23977                 
23978                 if(!e.shiftKey){
23979                     
23980                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
23981                     this.el.dom.setSelectionRange(end + 1, end + 1);
23982                     return;
23983                 }
23984                 
23985                 var f = value.substring(0, start).split("\t");
23986                 
23987                 if(f.pop().length != 0){
23988                     return;
23989                 }
23990                 
23991                 this.setValue(f.join("\t") + value.substring(end));
23992                 this.el.dom.setSelectionRange(start - 1, start - 1);
23993                 
23994             },
23995             
23996             "home" : function(e){
23997                 e.preventDefault();
23998                 
23999                 var curr = this.el.dom.selectionStart;
24000                 var lines = this.getValue().split("\n");
24001                 
24002                 if(!lines.length){
24003                     return;
24004                 }
24005                 
24006                 if(e.ctrlKey){
24007                     this.el.dom.setSelectionRange(0, 0);
24008                     return;
24009                 }
24010                 
24011                 var pos = 0;
24012                 
24013                 for (var i = 0; i < lines.length;i++) {
24014                     pos += lines[i].length;
24015                     
24016                     if(i != 0){
24017                         pos += 1;
24018                     }
24019                     
24020                     if(pos < curr){
24021                         continue;
24022                     }
24023                     
24024                     pos -= lines[i].length;
24025                     
24026                     break;
24027                 }
24028                 
24029                 if(!e.shiftKey){
24030                     this.el.dom.setSelectionRange(pos, pos);
24031                     return;
24032                 }
24033                 
24034                 this.el.dom.selectionStart = pos;
24035                 this.el.dom.selectionEnd = curr;
24036             },
24037             
24038             "end" : function(e){
24039                 e.preventDefault();
24040                 
24041                 var curr = this.el.dom.selectionStart;
24042                 var lines = this.getValue().split("\n");
24043                 
24044                 if(!lines.length){
24045                     return;
24046                 }
24047                 
24048                 if(e.ctrlKey){
24049                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
24050                     return;
24051                 }
24052                 
24053                 var pos = 0;
24054                 
24055                 for (var i = 0; i < lines.length;i++) {
24056                     
24057                     pos += lines[i].length;
24058                     
24059                     if(i != 0){
24060                         pos += 1;
24061                     }
24062                     
24063                     if(pos < curr){
24064                         continue;
24065                     }
24066                     
24067                     break;
24068                 }
24069                 
24070                 if(!e.shiftKey){
24071                     this.el.dom.setSelectionRange(pos, pos);
24072                     return;
24073                 }
24074                 
24075                 this.el.dom.selectionStart = curr;
24076                 this.el.dom.selectionEnd = pos;
24077             },
24078
24079             scope : this,
24080
24081             doRelay : function(foo, bar, hname){
24082                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
24083             },
24084
24085             forceKeyDown: true
24086         });
24087         
24088 //        if(this.autosave && this.w){
24089 //            this.autoSaveFn = setInterval(this.autosave, 1000);
24090 //        }
24091     },
24092
24093     // private
24094     onResize : function(w, h)
24095     {
24096         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24097         var ew = false;
24098         var eh = false;
24099         
24100         if(this.el ){
24101             if(typeof w == 'number'){
24102                 var aw = w - this.wrap.getFrameWidth('lr');
24103                 this.el.setWidth(this.adjustWidth('textarea', aw));
24104                 ew = aw;
24105             }
24106             if(typeof h == 'number'){
24107                 var tbh = 0;
24108                 for (var i =0; i < this.toolbars.length;i++) {
24109                     // fixme - ask toolbars for heights?
24110                     tbh += this.toolbars[i].tb.el.getHeight();
24111                     if (this.toolbars[i].footer) {
24112                         tbh += this.toolbars[i].footer.el.getHeight();
24113                     }
24114                 }
24115                 
24116                 
24117                 
24118                 
24119                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24120                 ah -= 5; // knock a few pixes off for look..
24121 //                Roo.log(ah);
24122                 this.el.setHeight(this.adjustWidth('textarea', ah));
24123                 var eh = ah;
24124             }
24125         }
24126         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24127         this.editorcore.onResize(ew,eh);
24128         
24129     },
24130
24131     /**
24132      * Toggles the editor between standard and source edit mode.
24133      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24134      */
24135     toggleSourceEdit : function(sourceEditMode)
24136     {
24137         this.editorcore.toggleSourceEdit(sourceEditMode);
24138         
24139         if(this.editorcore.sourceEditMode){
24140             Roo.log('editor - showing textarea');
24141             
24142 //            Roo.log('in');
24143 //            Roo.log(this.syncValue());
24144             this.editorcore.syncValue();
24145             this.el.removeClass('x-hidden');
24146             this.el.dom.removeAttribute('tabIndex');
24147             this.el.focus();
24148             this.el.dom.scrollTop = 0;
24149             
24150             
24151             for (var i = 0; i < this.toolbars.length; i++) {
24152                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
24153                     this.toolbars[i].tb.hide();
24154                     this.toolbars[i].footer.hide();
24155                 }
24156             }
24157             
24158         }else{
24159             Roo.log('editor - hiding textarea');
24160 //            Roo.log('out')
24161 //            Roo.log(this.pushValue()); 
24162             this.editorcore.pushValue();
24163             
24164             this.el.addClass('x-hidden');
24165             this.el.dom.setAttribute('tabIndex', -1);
24166             
24167             for (var i = 0; i < this.toolbars.length; i++) {
24168                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
24169                     this.toolbars[i].tb.show();
24170                     this.toolbars[i].footer.show();
24171                 }
24172             }
24173             
24174             //this.deferFocus();
24175         }
24176         
24177         this.setSize(this.wrap.getSize());
24178         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
24179         
24180         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24181     },
24182  
24183     // private (for BoxComponent)
24184     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24185
24186     // private (for BoxComponent)
24187     getResizeEl : function(){
24188         return this.wrap;
24189     },
24190
24191     // private (for BoxComponent)
24192     getPositionEl : function(){
24193         return this.wrap;
24194     },
24195
24196     // private
24197     initEvents : function(){
24198         this.originalValue = this.getValue();
24199     },
24200
24201     /**
24202      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24203      * @method
24204      */
24205     markInvalid : Roo.emptyFn,
24206     /**
24207      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24208      * @method
24209      */
24210     clearInvalid : Roo.emptyFn,
24211
24212     setValue : function(v){
24213         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24214         this.editorcore.pushValue();
24215     },
24216
24217      
24218     // private
24219     deferFocus : function(){
24220         this.focus.defer(10, this);
24221     },
24222
24223     // doc'ed in Field
24224     focus : function(){
24225         this.editorcore.focus();
24226         
24227     },
24228       
24229
24230     // private
24231     onDestroy : function(){
24232         
24233         
24234         
24235         if(this.rendered){
24236             
24237             for (var i =0; i < this.toolbars.length;i++) {
24238                 // fixme - ask toolbars for heights?
24239                 this.toolbars[i].onDestroy();
24240             }
24241             
24242             this.wrap.dom.innerHTML = '';
24243             this.wrap.remove();
24244         }
24245     },
24246
24247     // private
24248     onFirstFocus : function(){
24249         //Roo.log("onFirstFocus");
24250         this.editorcore.onFirstFocus();
24251          for (var i =0; i < this.toolbars.length;i++) {
24252             this.toolbars[i].onFirstFocus();
24253         }
24254         
24255     },
24256     
24257     // private
24258     syncValue : function()
24259     {
24260         this.editorcore.syncValue();
24261     },
24262     
24263     pushValue : function()
24264     {
24265         this.editorcore.pushValue();
24266     },
24267     
24268     setStylesheets : function(stylesheets)
24269     {
24270         this.editorcore.setStylesheets(stylesheets);
24271     },
24272     
24273     removeStylesheets : function()
24274     {
24275         this.editorcore.removeStylesheets();
24276     }
24277      
24278     
24279     // hide stuff that is not compatible
24280     /**
24281      * @event blur
24282      * @hide
24283      */
24284     /**
24285      * @event change
24286      * @hide
24287      */
24288     /**
24289      * @event focus
24290      * @hide
24291      */
24292     /**
24293      * @event specialkey
24294      * @hide
24295      */
24296     /**
24297      * @cfg {String} fieldClass @hide
24298      */
24299     /**
24300      * @cfg {String} focusClass @hide
24301      */
24302     /**
24303      * @cfg {String} autoCreate @hide
24304      */
24305     /**
24306      * @cfg {String} inputType @hide
24307      */
24308     /**
24309      * @cfg {String} invalidClass @hide
24310      */
24311     /**
24312      * @cfg {String} invalidText @hide
24313      */
24314     /**
24315      * @cfg {String} msgFx @hide
24316      */
24317     /**
24318      * @cfg {String} validateOnBlur @hide
24319      */
24320 });
24321  
24322     // <script type="text/javascript">
24323 /*
24324  * Based on
24325  * Ext JS Library 1.1.1
24326  * Copyright(c) 2006-2007, Ext JS, LLC.
24327  *  
24328  
24329  */
24330
24331 /**
24332  * @class Roo.form.HtmlEditorToolbar1
24333  * Basic Toolbar
24334  * 
24335  * Usage:
24336  *
24337  new Roo.form.HtmlEditor({
24338     ....
24339     toolbars : [
24340         new Roo.form.HtmlEditorToolbar1({
24341             disable : { fonts: 1 , format: 1, ..., ... , ...],
24342             btns : [ .... ]
24343         })
24344     }
24345      
24346  * 
24347  * @cfg {Object} disable List of elements to disable..
24348  * @cfg {Array} btns List of additional buttons.
24349  * 
24350  * 
24351  * NEEDS Extra CSS? 
24352  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24353  */
24354  
24355 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24356 {
24357     
24358     Roo.apply(this, config);
24359     
24360     // default disabled, based on 'good practice'..
24361     this.disable = this.disable || {};
24362     Roo.applyIf(this.disable, {
24363         fontSize : true,
24364         colors : true,
24365         specialElements : true
24366     });
24367     
24368     
24369     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24370     // dont call parent... till later.
24371 }
24372
24373 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24374     
24375     tb: false,
24376     
24377     rendered: false,
24378     
24379     editor : false,
24380     editorcore : false,
24381     /**
24382      * @cfg {Object} disable  List of toolbar elements to disable
24383          
24384      */
24385     disable : false,
24386     
24387     
24388      /**
24389      * @cfg {String} createLinkText The default text for the create link prompt
24390      */
24391     createLinkText : 'Please enter the URL for the link:',
24392     /**
24393      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24394      */
24395     defaultLinkValue : 'http:/'+'/',
24396    
24397     
24398       /**
24399      * @cfg {Array} fontFamilies An array of available font families
24400      */
24401     fontFamilies : [
24402         'Arial',
24403         'Courier New',
24404         'Tahoma',
24405         'Times New Roman',
24406         'Verdana'
24407     ],
24408     
24409     specialChars : [
24410            "&#169;",
24411           "&#174;",     
24412           "&#8482;",    
24413           "&#163;" ,    
24414          // "&#8212;",    
24415           "&#8230;",    
24416           "&#247;" ,    
24417         //  "&#225;" ,     ?? a acute?
24418            "&#8364;"    , //Euro
24419        //   "&#8220;"    ,
24420         //  "&#8221;"    ,
24421         //  "&#8226;"    ,
24422           "&#176;"  //   , // degrees
24423
24424          // "&#233;"     , // e ecute
24425          // "&#250;"     , // u ecute?
24426     ],
24427     
24428     specialElements : [
24429         {
24430             text: "Insert Table",
24431             xtype: 'MenuItem',
24432             xns : Roo.Menu,
24433             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
24434                 
24435         },
24436         {    
24437             text: "Insert Image",
24438             xtype: 'MenuItem',
24439             xns : Roo.Menu,
24440             ihtml : '<img src="about:blank"/>'
24441             
24442         }
24443         
24444          
24445     ],
24446     
24447     
24448     inputElements : [ 
24449             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24450             "input:submit", "input:button", "select", "textarea", "label" ],
24451     formats : [
24452         ["p"] ,  
24453         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24454         ["pre"],[ "code"], 
24455         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
24456         ['div'],['span'],
24457         ['sup'],['sub']
24458     ],
24459     
24460     cleanStyles : [
24461         "font-size"
24462     ],
24463      /**
24464      * @cfg {String} defaultFont default font to use.
24465      */
24466     defaultFont: 'tahoma',
24467    
24468     fontSelect : false,
24469     
24470     
24471     formatCombo : false,
24472     
24473     init : function(editor)
24474     {
24475         this.editor = editor;
24476         this.editorcore = editor.editorcore ? editor.editorcore : editor;
24477         var editorcore = this.editorcore;
24478         
24479         var _t = this;
24480         
24481         var fid = editorcore.frameId;
24482         var etb = this;
24483         function btn(id, toggle, handler){
24484             var xid = fid + '-'+ id ;
24485             return {
24486                 id : xid,
24487                 cmd : id,
24488                 cls : 'x-btn-icon x-edit-'+id,
24489                 enableToggle:toggle !== false,
24490                 scope: _t, // was editor...
24491                 handler:handler||_t.relayBtnCmd,
24492                 clickEvent:'mousedown',
24493                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24494                 tabIndex:-1
24495             };
24496         }
24497         
24498         
24499         
24500         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24501         this.tb = tb;
24502          // stop form submits
24503         tb.el.on('click', function(e){
24504             e.preventDefault(); // what does this do?
24505         });
24506
24507         if(!this.disable.font) { // && !Roo.isSafari){
24508             /* why no safari for fonts 
24509             editor.fontSelect = tb.el.createChild({
24510                 tag:'select',
24511                 tabIndex: -1,
24512                 cls:'x-font-select',
24513                 html: this.createFontOptions()
24514             });
24515             
24516             editor.fontSelect.on('change', function(){
24517                 var font = editor.fontSelect.dom.value;
24518                 editor.relayCmd('fontname', font);
24519                 editor.deferFocus();
24520             }, editor);
24521             
24522             tb.add(
24523                 editor.fontSelect.dom,
24524                 '-'
24525             );
24526             */
24527             
24528         };
24529         if(!this.disable.formats){
24530             this.formatCombo = new Roo.form.ComboBox({
24531                 store: new Roo.data.SimpleStore({
24532                     id : 'tag',
24533                     fields: ['tag'],
24534                     data : this.formats // from states.js
24535                 }),
24536                 blockFocus : true,
24537                 name : '',
24538                 //autoCreate : {tag: "div",  size: "20"},
24539                 displayField:'tag',
24540                 typeAhead: false,
24541                 mode: 'local',
24542                 editable : false,
24543                 triggerAction: 'all',
24544                 emptyText:'Add tag',
24545                 selectOnFocus:true,
24546                 width:135,
24547                 listeners : {
24548                     'select': function(c, r, i) {
24549                         editorcore.insertTag(r.get('tag'));
24550                         editor.focus();
24551                     }
24552                 }
24553
24554             });
24555             tb.addField(this.formatCombo);
24556             
24557         }
24558         
24559         if(!this.disable.format){
24560             tb.add(
24561                 btn('bold'),
24562                 btn('italic'),
24563                 btn('underline'),
24564                 btn('strikethrough')
24565             );
24566         };
24567         if(!this.disable.fontSize){
24568             tb.add(
24569                 '-',
24570                 
24571                 
24572                 btn('increasefontsize', false, editorcore.adjustFont),
24573                 btn('decreasefontsize', false, editorcore.adjustFont)
24574             );
24575         };
24576         
24577         
24578         if(!this.disable.colors){
24579             tb.add(
24580                 '-', {
24581                     id:editorcore.frameId +'-forecolor',
24582                     cls:'x-btn-icon x-edit-forecolor',
24583                     clickEvent:'mousedown',
24584                     tooltip: this.buttonTips['forecolor'] || undefined,
24585                     tabIndex:-1,
24586                     menu : new Roo.menu.ColorMenu({
24587                         allowReselect: true,
24588                         focus: Roo.emptyFn,
24589                         value:'000000',
24590                         plain:true,
24591                         selectHandler: function(cp, color){
24592                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
24593                             editor.deferFocus();
24594                         },
24595                         scope: editorcore,
24596                         clickEvent:'mousedown'
24597                     })
24598                 }, {
24599                     id:editorcore.frameId +'backcolor',
24600                     cls:'x-btn-icon x-edit-backcolor',
24601                     clickEvent:'mousedown',
24602                     tooltip: this.buttonTips['backcolor'] || undefined,
24603                     tabIndex:-1,
24604                     menu : new Roo.menu.ColorMenu({
24605                         focus: Roo.emptyFn,
24606                         value:'FFFFFF',
24607                         plain:true,
24608                         allowReselect: true,
24609                         selectHandler: function(cp, color){
24610                             if(Roo.isGecko){
24611                                 editorcore.execCmd('useCSS', false);
24612                                 editorcore.execCmd('hilitecolor', color);
24613                                 editorcore.execCmd('useCSS', true);
24614                                 editor.deferFocus();
24615                             }else{
24616                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
24617                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
24618                                 editor.deferFocus();
24619                             }
24620                         },
24621                         scope:editorcore,
24622                         clickEvent:'mousedown'
24623                     })
24624                 }
24625             );
24626         };
24627         // now add all the items...
24628         
24629
24630         if(!this.disable.alignments){
24631             tb.add(
24632                 '-',
24633                 btn('justifyleft'),
24634                 btn('justifycenter'),
24635                 btn('justifyright')
24636             );
24637         };
24638
24639         //if(!Roo.isSafari){
24640             if(!this.disable.links){
24641                 tb.add(
24642                     '-',
24643                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
24644                 );
24645             };
24646
24647             if(!this.disable.lists){
24648                 tb.add(
24649                     '-',
24650                     btn('insertorderedlist'),
24651                     btn('insertunorderedlist')
24652                 );
24653             }
24654             if(!this.disable.sourceEdit){
24655                 tb.add(
24656                     '-',
24657                     btn('sourceedit', true, function(btn){
24658                         this.toggleSourceEdit(btn.pressed);
24659                     })
24660                 );
24661             }
24662         //}
24663         
24664         var smenu = { };
24665         // special menu.. - needs to be tidied up..
24666         if (!this.disable.special) {
24667             smenu = {
24668                 text: "&#169;",
24669                 cls: 'x-edit-none',
24670                 
24671                 menu : {
24672                     items : []
24673                 }
24674             };
24675             for (var i =0; i < this.specialChars.length; i++) {
24676                 smenu.menu.items.push({
24677                     
24678                     html: this.specialChars[i],
24679                     handler: function(a,b) {
24680                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
24681                         //editor.insertAtCursor(a.html);
24682                         
24683                     },
24684                     tabIndex:-1
24685                 });
24686             }
24687             
24688             
24689             tb.add(smenu);
24690             
24691             
24692         }
24693         
24694         var cmenu = { };
24695         if (!this.disable.cleanStyles) {
24696             cmenu = {
24697                 cls: 'x-btn-icon x-btn-clear',
24698                 
24699                 menu : {
24700                     items : []
24701                 }
24702             };
24703             for (var i =0; i < this.cleanStyles.length; i++) {
24704                 cmenu.menu.items.push({
24705                     actiontype : this.cleanStyles[i],
24706                     html: 'Remove ' + this.cleanStyles[i],
24707                     handler: function(a,b) {
24708 //                        Roo.log(a);
24709 //                        Roo.log(b);
24710                         var c = Roo.get(editorcore.doc.body);
24711                         c.select('[style]').each(function(s) {
24712                             s.dom.style.removeProperty(a.actiontype);
24713                         });
24714                         editorcore.syncValue();
24715                     },
24716                     tabIndex:-1
24717                 });
24718             }
24719             cmenu.menu.items.push({
24720                 actiontype : 'tablewidths',
24721                 html: 'Remove Table Widths',
24722                 handler: function(a,b) {
24723                     editorcore.cleanTableWidths();
24724                     editorcore.syncValue();
24725                 },
24726                 tabIndex:-1
24727             });
24728             cmenu.menu.items.push({
24729                 actiontype : 'word',
24730                 html: 'Remove MS Word Formating',
24731                 handler: function(a,b) {
24732                     editorcore.cleanWord();
24733                     editorcore.syncValue();
24734                 },
24735                 tabIndex:-1
24736             });
24737             
24738             cmenu.menu.items.push({
24739                 actiontype : 'all',
24740                 html: 'Remove All Styles',
24741                 handler: function(a,b) {
24742                     
24743                     var c = Roo.get(editorcore.doc.body);
24744                     c.select('[style]').each(function(s) {
24745                         s.dom.removeAttribute('style');
24746                     });
24747                     editorcore.syncValue();
24748                 },
24749                 tabIndex:-1
24750             });
24751             
24752             cmenu.menu.items.push({
24753                 actiontype : 'all',
24754                 html: 'Remove All CSS Classes',
24755                 handler: function(a,b) {
24756                     
24757                     var c = Roo.get(editorcore.doc.body);
24758                     c.select('[class]').each(function(s) {
24759                         s.dom.removeAttribute('class');
24760                     });
24761                     editorcore.cleanWord();
24762                     editorcore.syncValue();
24763                 },
24764                 tabIndex:-1
24765             });
24766             
24767              cmenu.menu.items.push({
24768                 actiontype : 'tidy',
24769                 html: 'Tidy HTML Source',
24770                 handler: function(a,b) {
24771                     new Roo.htmleditor.Tidy(editorcore.doc.body);
24772                     editorcore.syncValue();
24773                 },
24774                 tabIndex:-1
24775             });
24776             
24777             
24778             tb.add(cmenu);
24779         }
24780          
24781         if (!this.disable.specialElements) {
24782             var semenu = {
24783                 text: "Other;",
24784                 cls: 'x-edit-none',
24785                 menu : {
24786                     items : []
24787                 }
24788             };
24789             for (var i =0; i < this.specialElements.length; i++) {
24790                 semenu.menu.items.push(
24791                     Roo.apply({ 
24792                         handler: function(a,b) {
24793                             editor.insertAtCursor(this.ihtml);
24794                         }
24795                     }, this.specialElements[i])
24796                 );
24797                     
24798             }
24799             
24800             tb.add(semenu);
24801             
24802             
24803         }
24804          
24805         
24806         if (this.btns) {
24807             for(var i =0; i< this.btns.length;i++) {
24808                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
24809                 b.cls =  'x-edit-none';
24810                 
24811                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
24812                     b.cls += ' x-init-enable';
24813                 }
24814                 
24815                 b.scope = editorcore;
24816                 tb.add(b);
24817             }
24818         
24819         }
24820         
24821         
24822         
24823         // disable everything...
24824         
24825         this.tb.items.each(function(item){
24826             
24827            if(
24828                 item.id != editorcore.frameId+ '-sourceedit' && 
24829                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
24830             ){
24831                 
24832                 item.disable();
24833             }
24834         });
24835         this.rendered = true;
24836         
24837         // the all the btns;
24838         editor.on('editorevent', this.updateToolbar, this);
24839         // other toolbars need to implement this..
24840         //editor.on('editmodechange', this.updateToolbar, this);
24841     },
24842     
24843     
24844     relayBtnCmd : function(btn) {
24845         this.editorcore.relayCmd(btn.cmd);
24846     },
24847     // private used internally
24848     createLink : function(){
24849         Roo.log("create link?");
24850         var url = prompt(this.createLinkText, this.defaultLinkValue);
24851         if(url && url != 'http:/'+'/'){
24852             this.editorcore.relayCmd('createlink', url);
24853         }
24854     },
24855
24856     
24857     /**
24858      * Protected method that will not generally be called directly. It triggers
24859      * a toolbar update by reading the markup state of the current selection in the editor.
24860      */
24861     updateToolbar: function(){
24862
24863         if(!this.editorcore.activated){
24864             this.editor.onFirstFocus();
24865             return;
24866         }
24867
24868         var btns = this.tb.items.map, 
24869             doc = this.editorcore.doc,
24870             frameId = this.editorcore.frameId;
24871
24872         if(!this.disable.font && !Roo.isSafari){
24873             /*
24874             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
24875             if(name != this.fontSelect.dom.value){
24876                 this.fontSelect.dom.value = name;
24877             }
24878             */
24879         }
24880         if(!this.disable.format){
24881             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
24882             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
24883             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
24884             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
24885         }
24886         if(!this.disable.alignments){
24887             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
24888             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
24889             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
24890         }
24891         if(!Roo.isSafari && !this.disable.lists){
24892             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
24893             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
24894         }
24895         
24896         var ans = this.editorcore.getAllAncestors();
24897         if (this.formatCombo) {
24898             
24899             
24900             var store = this.formatCombo.store;
24901             this.formatCombo.setValue("");
24902             for (var i =0; i < ans.length;i++) {
24903                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24904                     // select it..
24905                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24906                     break;
24907                 }
24908             }
24909         }
24910         
24911         
24912         
24913         // hides menus... - so this cant be on a menu...
24914         Roo.menu.MenuMgr.hideAll();
24915
24916         //this.editorsyncValue();
24917     },
24918    
24919     
24920     createFontOptions : function(){
24921         var buf = [], fs = this.fontFamilies, ff, lc;
24922         
24923         
24924         
24925         for(var i = 0, len = fs.length; i< len; i++){
24926             ff = fs[i];
24927             lc = ff.toLowerCase();
24928             buf.push(
24929                 '<option value="',lc,'" style="font-family:',ff,';"',
24930                     (this.defaultFont == lc ? ' selected="true">' : '>'),
24931                     ff,
24932                 '</option>'
24933             );
24934         }
24935         return buf.join('');
24936     },
24937     
24938     toggleSourceEdit : function(sourceEditMode){
24939         
24940         Roo.log("toolbar toogle");
24941         if(sourceEditMode === undefined){
24942             sourceEditMode = !this.sourceEditMode;
24943         }
24944         this.sourceEditMode = sourceEditMode === true;
24945         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
24946         // just toggle the button?
24947         if(btn.pressed !== this.sourceEditMode){
24948             btn.toggle(this.sourceEditMode);
24949             return;
24950         }
24951         
24952         if(sourceEditMode){
24953             Roo.log("disabling buttons");
24954             this.tb.items.each(function(item){
24955                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
24956                     item.disable();
24957                 }
24958             });
24959           
24960         }else{
24961             Roo.log("enabling buttons");
24962             if(this.editorcore.initialized){
24963                 this.tb.items.each(function(item){
24964                     item.enable();
24965                 });
24966             }
24967             
24968         }
24969         Roo.log("calling toggole on editor");
24970         // tell the editor that it's been pressed..
24971         this.editor.toggleSourceEdit(sourceEditMode);
24972        
24973     },
24974      /**
24975      * Object collection of toolbar tooltips for the buttons in the editor. The key
24976      * is the command id associated with that button and the value is a valid QuickTips object.
24977      * For example:
24978 <pre><code>
24979 {
24980     bold : {
24981         title: 'Bold (Ctrl+B)',
24982         text: 'Make the selected text bold.',
24983         cls: 'x-html-editor-tip'
24984     },
24985     italic : {
24986         title: 'Italic (Ctrl+I)',
24987         text: 'Make the selected text italic.',
24988         cls: 'x-html-editor-tip'
24989     },
24990     ...
24991 </code></pre>
24992     * @type Object
24993      */
24994     buttonTips : {
24995         bold : {
24996             title: 'Bold (Ctrl+B)',
24997             text: 'Make the selected text bold.',
24998             cls: 'x-html-editor-tip'
24999         },
25000         italic : {
25001             title: 'Italic (Ctrl+I)',
25002             text: 'Make the selected text italic.',
25003             cls: 'x-html-editor-tip'
25004         },
25005         underline : {
25006             title: 'Underline (Ctrl+U)',
25007             text: 'Underline the selected text.',
25008             cls: 'x-html-editor-tip'
25009         },
25010         strikethrough : {
25011             title: 'Strikethrough',
25012             text: 'Strikethrough the selected text.',
25013             cls: 'x-html-editor-tip'
25014         },
25015         increasefontsize : {
25016             title: 'Grow Text',
25017             text: 'Increase the font size.',
25018             cls: 'x-html-editor-tip'
25019         },
25020         decreasefontsize : {
25021             title: 'Shrink Text',
25022             text: 'Decrease the font size.',
25023             cls: 'x-html-editor-tip'
25024         },
25025         backcolor : {
25026             title: 'Text Highlight Color',
25027             text: 'Change the background color of the selected text.',
25028             cls: 'x-html-editor-tip'
25029         },
25030         forecolor : {
25031             title: 'Font Color',
25032             text: 'Change the color of the selected text.',
25033             cls: 'x-html-editor-tip'
25034         },
25035         justifyleft : {
25036             title: 'Align Text Left',
25037             text: 'Align text to the left.',
25038             cls: 'x-html-editor-tip'
25039         },
25040         justifycenter : {
25041             title: 'Center Text',
25042             text: 'Center text in the editor.',
25043             cls: 'x-html-editor-tip'
25044         },
25045         justifyright : {
25046             title: 'Align Text Right',
25047             text: 'Align text to the right.',
25048             cls: 'x-html-editor-tip'
25049         },
25050         insertunorderedlist : {
25051             title: 'Bullet List',
25052             text: 'Start a bulleted list.',
25053             cls: 'x-html-editor-tip'
25054         },
25055         insertorderedlist : {
25056             title: 'Numbered List',
25057             text: 'Start a numbered list.',
25058             cls: 'x-html-editor-tip'
25059         },
25060         createlink : {
25061             title: 'Hyperlink',
25062             text: 'Make the selected text a hyperlink.',
25063             cls: 'x-html-editor-tip'
25064         },
25065         sourceedit : {
25066             title: 'Source Edit',
25067             text: 'Switch to source editing mode.',
25068             cls: 'x-html-editor-tip'
25069         }
25070     },
25071     // private
25072     onDestroy : function(){
25073         if(this.rendered){
25074             
25075             this.tb.items.each(function(item){
25076                 if(item.menu){
25077                     item.menu.removeAll();
25078                     if(item.menu.el){
25079                         item.menu.el.destroy();
25080                     }
25081                 }
25082                 item.destroy();
25083             });
25084              
25085         }
25086     },
25087     onFirstFocus: function() {
25088         this.tb.items.each(function(item){
25089            item.enable();
25090         });
25091     }
25092 });
25093
25094
25095
25096
25097 // <script type="text/javascript">
25098 /*
25099  * Based on
25100  * Ext JS Library 1.1.1
25101  * Copyright(c) 2006-2007, Ext JS, LLC.
25102  *  
25103  
25104  */
25105
25106  
25107 /**
25108  * @class Roo.form.HtmlEditor.ToolbarContext
25109  * Context Toolbar
25110  * 
25111  * Usage:
25112  *
25113  new Roo.form.HtmlEditor({
25114     ....
25115     toolbars : [
25116         { xtype: 'ToolbarStandard', styles : {} }
25117         { xtype: 'ToolbarContext', disable : {} }
25118     ]
25119 })
25120
25121      
25122  * 
25123  * @config : {Object} disable List of elements to disable.. (not done yet.)
25124  * @config : {Object} styles  Map of styles available.
25125  * 
25126  */
25127
25128 Roo.form.HtmlEditor.ToolbarContext = function(config)
25129 {
25130     
25131     Roo.apply(this, config);
25132     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25133     // dont call parent... till later.
25134     this.styles = this.styles || {};
25135 }
25136
25137  
25138
25139 Roo.form.HtmlEditor.ToolbarContext.types = {
25140     'IMG' : {
25141         width : {
25142             title: "Width",
25143             width: 40
25144         },
25145         height:  {
25146             title: "Height",
25147             width: 40
25148         },
25149         align: {
25150             title: "Align",
25151             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25152             width : 80
25153             
25154         },
25155         border: {
25156             title: "Border",
25157             width: 40
25158         },
25159         alt: {
25160             title: "Alt",
25161             width: 120
25162         },
25163         src : {
25164             title: "Src",
25165             width: 220
25166         }
25167         
25168     },
25169     
25170     'FIGURE' : {
25171          align: {
25172             title: "Align",
25173             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25174             width : 80  
25175         }
25176     },
25177     'A' : {
25178         name : {
25179             title: "Name",
25180             width: 50
25181         },
25182         target:  {
25183             title: "Target",
25184             width: 120
25185         },
25186         href:  {
25187             title: "Href",
25188             width: 220
25189         } // border?
25190         
25191     },
25192     /*
25193     'TABLE' : {
25194         rows : {
25195             title: "Rows",
25196             width: 20
25197         },
25198         cols : {
25199             title: "Cols",
25200             width: 20
25201         },
25202         width : {
25203             title: "Width",
25204             width: 40
25205         },
25206         height : {
25207             title: "Height",
25208             width: 40
25209         },
25210         border : {
25211             title: "Border",
25212             width: 20
25213         }
25214     },
25215     'TD' : {
25216         width : {
25217             title: "Width",
25218             width: 40
25219         },
25220         height : {
25221             title: "Height",
25222             width: 40
25223         },   
25224         align: {
25225             title: "Align",
25226             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25227             width: 80
25228         },
25229         valign: {
25230             title: "Valign",
25231             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25232             width: 80
25233         },
25234         colspan: {
25235             title: "Colspan",
25236             width: 20
25237             
25238         },
25239          'font-family'  : {
25240             title : "Font",
25241             style : 'fontFamily',
25242             displayField: 'display',
25243             optname : 'font-family',
25244             width: 140
25245         }
25246     },
25247     */
25248     'INPUT' : {
25249         name : {
25250             title: "name",
25251             width: 120
25252         },
25253         value : {
25254             title: "Value",
25255             width: 120
25256         },
25257         width : {
25258             title: "Width",
25259             width: 40
25260         }
25261     },
25262     'LABEL' : {
25263         'for' : {
25264             title: "For",
25265             width: 120
25266         }
25267     },
25268     'TEXTAREA' : {
25269         name : {
25270             title: "name",
25271             width: 120
25272         },
25273         rows : {
25274             title: "Rows",
25275             width: 20
25276         },
25277         cols : {
25278             title: "Cols",
25279             width: 20
25280         }
25281     },
25282     'SELECT' : {
25283         name : {
25284             title: "name",
25285             width: 120
25286         },
25287         selectoptions : {
25288             title: "Options",
25289             width: 200
25290         }
25291     },
25292     
25293     // should we really allow this??
25294     // should this just be 
25295     'BODY' : {
25296         title : {
25297             title: "Title",
25298             width: 200,
25299             disabled : true
25300         }
25301     },
25302     /*
25303     'SPAN' : {
25304         'font-family'  : {
25305             title : "Font",
25306             style : 'fontFamily',
25307             displayField: 'display',
25308             optname : 'font-family',
25309             width: 140
25310         }
25311     },
25312     'DIV' : {
25313         'font-family'  : {
25314             title : "Font",
25315             style : 'fontFamily',
25316             displayField: 'display',
25317             optname : 'font-family',
25318             width: 140
25319         }
25320     },
25321      'P' : {
25322         'font-family'  : {
25323             title : "Font",
25324             style : 'fontFamily',
25325             displayField: 'display',
25326             optname : 'font-family',
25327             width: 140
25328         }
25329     },
25330     */
25331     '*' : {
25332         // empty..
25333     }
25334
25335 };
25336
25337 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
25338 Roo.form.HtmlEditor.ToolbarContext.stores = false;
25339
25340 Roo.form.HtmlEditor.ToolbarContext.options = {
25341         'font-family'  : [ 
25342                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
25343                 [ 'Courier New', 'Courier New'],
25344                 [ 'Tahoma', 'Tahoma'],
25345                 [ 'Times New Roman,serif', 'Times'],
25346                 [ 'Verdana','Verdana' ]
25347         ]
25348 };
25349
25350 // fixme - these need to be configurable..
25351  
25352
25353 //Roo.form.HtmlEditor.ToolbarContext.types
25354
25355
25356 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25357     
25358     tb: false,
25359     
25360     rendered: false,
25361     
25362     editor : false,
25363     editorcore : false,
25364     /**
25365      * @cfg {Object} disable  List of toolbar elements to disable
25366          
25367      */
25368     disable : false,
25369     /**
25370      * @cfg {Object} styles List of styles 
25371      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
25372      *
25373      * These must be defined in the page, so they get rendered correctly..
25374      * .headline { }
25375      * TD.underline { }
25376      * 
25377      */
25378     styles : false,
25379     
25380     options: false,
25381     
25382     toolbars : false,
25383     
25384     init : function(editor)
25385     {
25386         this.editor = editor;
25387         this.editorcore = editor.editorcore ? editor.editorcore : editor;
25388         var editorcore = this.editorcore;
25389         
25390         var fid = editorcore.frameId;
25391         var etb = this;
25392         function btn(id, toggle, handler){
25393             var xid = fid + '-'+ id ;
25394             return {
25395                 id : xid,
25396                 cmd : id,
25397                 cls : 'x-btn-icon x-edit-'+id,
25398                 enableToggle:toggle !== false,
25399                 scope: editorcore, // was editor...
25400                 handler:handler||editorcore.relayBtnCmd,
25401                 clickEvent:'mousedown',
25402                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25403                 tabIndex:-1
25404             };
25405         }
25406         // create a new element.
25407         var wdiv = editor.wrap.createChild({
25408                 tag: 'div'
25409             }, editor.wrap.dom.firstChild.nextSibling, true);
25410         
25411         // can we do this more than once??
25412         
25413          // stop form submits
25414       
25415  
25416         // disable everything...
25417         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25418         this.toolbars = {};
25419            
25420         for (var i in  ty) {
25421           
25422             this.toolbars[i] = this.buildToolbar(ty[i],i);
25423         }
25424         this.tb = this.toolbars.BODY;
25425         this.tb.el.show();
25426         this.buildFooter();
25427         this.footer.show();
25428         editor.on('hide', function( ) { this.footer.hide() }, this);
25429         editor.on('show', function( ) { this.footer.show() }, this);
25430         
25431          
25432         this.rendered = true;
25433         
25434         // the all the btns;
25435         editor.on('editorevent', this.updateToolbar, this);
25436         // other toolbars need to implement this..
25437         //editor.on('editmodechange', this.updateToolbar, this);
25438     },
25439     
25440     
25441     
25442     /**
25443      * Protected method that will not generally be called directly. It triggers
25444      * a toolbar update by reading the markup state of the current selection in the editor.
25445      *
25446      * Note you can force an update by calling on('editorevent', scope, false)
25447      */
25448     updateToolbar: function(editor ,ev, sel)
25449     {
25450         
25451         if (ev) {
25452             ev.stopEvent(); // se if we can stop this looping with mutiple events.
25453         }
25454         
25455         //Roo.log(ev);
25456         // capture mouse up - this is handy for selecting images..
25457         // perhaps should go somewhere else...
25458         if(!this.editorcore.activated){
25459              this.editor.onFirstFocus();
25460             return;
25461         }
25462         Roo.log(ev ? ev.target : 'NOTARGET');
25463         
25464         
25465         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
25466         // selectNode - might want to handle IE?
25467         
25468         
25469         
25470         if (ev &&
25471             (ev.type == 'mouseup' || ev.type == 'click' ) &&
25472             ev.target && ev.target != 'BODY' ) { // && ev.target.tagName == 'IMG') {
25473             // they have click on an image...
25474             // let's see if we can change the selection...
25475             sel = ev.target;
25476             
25477             // this triggers looping?
25478             //this.editorcore.selectNode(sel);
25479              
25480         }  
25481         
25482       
25483         //var updateFooter = sel ? false : true; 
25484         
25485         
25486         var ans = this.editorcore.getAllAncestors();
25487         
25488         // pick
25489         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
25490         
25491         if (!sel) { 
25492             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
25493             sel = sel ? sel : this.editorcore.doc.body;
25494             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
25495             
25496         }
25497         
25498         var tn = sel.tagName.toUpperCase();
25499         var lastSel = this.tb.selectedNode;
25500         this.tb.selectedNode = sel;
25501         var left_label = tn;
25502         
25503         // ok see if we are editing a block?
25504         var sel_el = Roo.get(sel);
25505         var db = false;
25506         // you are not actually selecting the block.
25507         if (sel && sel.hasAttribute('data-block')) {
25508             db = sel;
25509         } else if (sel && !sel.hasAttribute('contenteditable')) {
25510             db = sel_el.findParent('[data-block]');
25511             var cepar = sel_el.findParent('[contenteditable=true]');
25512             if (db && cepar && cepar.tagName != 'BODY') {
25513                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
25514             }   
25515         }
25516         
25517         
25518         var block = false;
25519         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
25520         if (db) {
25521             block = Roo.htmleditor.Block.factory(db);
25522             if (block) {
25523                 tn = 'BLOCK.' + db.getAttribute('data-block');
25524                 
25525                 //this.editorcore.selectNode(db);
25526                 if (typeof(this.toolbars[tn]) == 'undefined') {
25527                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
25528                 }
25529                 this.toolbars[tn].selectedNode = db;
25530                 left_label = block.friendly_name;
25531                 ans = this.editorcore.getAllAncestors();
25532             }
25533             
25534                 
25535             
25536         }
25537         
25538         
25539         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
25540             return; // no change?
25541         }
25542         
25543         
25544           
25545         this.tb.el.hide();
25546         ///console.log("show: " + tn);
25547         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
25548         
25549         this.tb.el.show();
25550         // update name
25551         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
25552         
25553         
25554         // update attributes
25555         if (block) {
25556              
25557             this.tb.fields.each(function(e) {
25558                 e.setValue(block[e.name]);
25559             });
25560             
25561             
25562         } else  if (this.tb.fields && this.tb.selectedNode) {
25563             this.tb.fields.each( function(e) {
25564                 if (e.stylename) {
25565                     e.setValue(this.tb.selectedNode.style[e.stylename]);
25566                     return;
25567                 } 
25568                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
25569             }, this);
25570             this.updateToolbarStyles(this.tb.selectedNode);  
25571         }
25572         
25573         
25574        
25575         Roo.menu.MenuMgr.hideAll();
25576
25577         
25578         
25579     
25580         // update the footer
25581         //
25582         this.updateFooter(ans);
25583              
25584     },
25585     
25586     updateToolbarStyles : function(sel)
25587     {
25588         var hasStyles = false;
25589         for(var i in this.styles) {
25590             hasStyles = true;
25591             break;
25592         }
25593         
25594         // update styles
25595         if (hasStyles && this.tb.hasStyles) { 
25596             var st = this.tb.fields.item(0);
25597             
25598             st.store.removeAll();
25599             var cn = sel.className.split(/\s+/);
25600             
25601             var avs = [];
25602             if (this.styles['*']) {
25603                 
25604                 Roo.each(this.styles['*'], function(v) {
25605                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
25606                 });
25607             }
25608             if (this.styles[tn]) { 
25609                 Roo.each(this.styles[tn], function(v) {
25610                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
25611                 });
25612             }
25613             
25614             st.store.loadData(avs);
25615             st.collapse();
25616             st.setValue(cn);
25617         }
25618     },
25619     
25620      
25621     updateFooter : function(ans)
25622     {
25623         var html = '';
25624         if (ans === false) {
25625             this.footDisp.dom.innerHTML = '';
25626             return;
25627         }
25628         
25629         this.footerEls = ans.reverse();
25630         Roo.each(this.footerEls, function(a,i) {
25631             if (!a) { return; }
25632             html += html.length ? ' &gt; '  :  '';
25633             
25634             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
25635             
25636         });
25637        
25638         // 
25639         var sz = this.footDisp.up('td').getSize();
25640         this.footDisp.dom.style.width = (sz.width -10) + 'px';
25641         this.footDisp.dom.style.marginLeft = '5px';
25642         
25643         this.footDisp.dom.style.overflow = 'hidden';
25644         
25645         this.footDisp.dom.innerHTML = html;
25646             
25647         
25648     },
25649    
25650        
25651     // private
25652     onDestroy : function(){
25653         if(this.rendered){
25654             
25655             this.tb.items.each(function(item){
25656                 if(item.menu){
25657                     item.menu.removeAll();
25658                     if(item.menu.el){
25659                         item.menu.el.destroy();
25660                     }
25661                 }
25662                 item.destroy();
25663             });
25664              
25665         }
25666     },
25667     onFirstFocus: function() {
25668         // need to do this for all the toolbars..
25669         this.tb.items.each(function(item){
25670            item.enable();
25671         });
25672     },
25673     buildToolbar: function(tlist, nm, friendly_name, block)
25674     {
25675         var editor = this.editor;
25676         var editorcore = this.editorcore;
25677          // create a new element.
25678         var wdiv = editor.wrap.createChild({
25679                 tag: 'div'
25680             }, editor.wrap.dom.firstChild.nextSibling, true);
25681         
25682        
25683         var tb = new Roo.Toolbar(wdiv);
25684         this.tb = tb;
25685         if (tlist === false && block) {
25686             tlist = block.contextMenu(this);
25687         }
25688         
25689         tb.hasStyles = false;
25690         tb.name = nm;
25691         
25692         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
25693         
25694         var styles = Array.from(this.styles);
25695         
25696         
25697         // styles...
25698         if (styles && styles.length) {
25699             tb.hasStyles = true;
25700             // this needs a multi-select checkbox...
25701             tb.addField( new Roo.form.ComboBox({
25702                 store: new Roo.data.SimpleStore({
25703                     id : 'val',
25704                     fields: ['val', 'selected'],
25705                     data : [] 
25706                 }),
25707                 name : '-roo-edit-className',
25708                 attrname : 'className',
25709                 displayField: 'val',
25710                 typeAhead: false,
25711                 mode: 'local',
25712                 editable : false,
25713                 triggerAction: 'all',
25714                 emptyText:'Select Style',
25715                 selectOnFocus:true,
25716                 width: 130,
25717                 listeners : {
25718                     'select': function(c, r, i) {
25719                         // initial support only for on class per el..
25720                         tb.selectedNode.className =  r ? r.get('val') : '';
25721                         editorcore.syncValue();
25722                     }
25723                 }
25724     
25725             }));
25726         }
25727         
25728         var tbc = Roo.form.HtmlEditor.ToolbarContext;
25729         
25730         
25731         for (var i in tlist) {
25732             
25733             // newer versions will use xtype cfg to create menus.
25734             if (typeof(tlist[i].xtype) != 'undefined') {
25735                 
25736                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
25737                 
25738                 
25739                 continue;
25740             }
25741             
25742             var item = tlist[i];
25743             tb.add(item.title + ":&nbsp;");
25744             
25745             
25746             //optname == used so you can configure the options available..
25747             var opts = item.opts ? item.opts : false;
25748             if (item.optname) { // use the b
25749                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
25750            
25751             }
25752             
25753             if (opts) {
25754                 // opts == pulldown..
25755                 tb.addField( new Roo.form.ComboBox({
25756                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
25757                         id : 'val',
25758                         fields: ['val', 'display'],
25759                         data : opts  
25760                     }),
25761                     name : '-roo-edit-' + i,
25762                     
25763                     attrname : i,
25764                     stylename : item.style ? item.style : false,
25765                     
25766                     displayField: item.displayField ? item.displayField : 'val',
25767                     valueField :  'val',
25768                     typeAhead: false,
25769                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
25770                     editable : false,
25771                     triggerAction: 'all',
25772                     emptyText:'Select',
25773                     selectOnFocus:true,
25774                     width: item.width ? item.width  : 130,
25775                     listeners : {
25776                         'select': function(c, r, i) {
25777                             if (tb.selectedNode.hasAttribute('data-block')) {
25778                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
25779                                 b[c.attrname] = r.get('val');
25780                                 b.updateElement(tb.selectedNode);
25781                                 editorcore.syncValue();
25782                                 return;
25783                             }
25784                             
25785                             if (c.stylename) {
25786                                 tb.selectedNode.style[c.stylename] =  r.get('val');
25787                                 editorcore.syncValue();
25788                                 return;
25789                             }
25790                             if (r === false) {
25791                                 tb.selectedNode.removeAttribute(c.attrname);
25792                                 editorcore.syncValue();
25793                                 return;
25794                             }
25795                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
25796                             editorcore.syncValue();
25797                         }
25798                     }
25799
25800                 }));
25801                 continue;
25802                     
25803                  
25804                 /*
25805                 tb.addField( new Roo.form.TextField({
25806                     name: i,
25807                     width: 100,
25808                     //allowBlank:false,
25809                     value: ''
25810                 }));
25811                 continue;
25812                 */
25813             }
25814             tb.addField( new Roo.form.TextField({
25815                 name: '-roo-edit-' + i,
25816                 attrname : i,
25817                 
25818                 width: item.width,
25819                 //allowBlank:true,
25820                 value: '',
25821                 listeners: {
25822                     'change' : function(f, nv, ov) {
25823                         
25824                         if (tb.selectedNode.hasAttribute('data-block')) {
25825                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
25826                             b[f.attrname] = nv;
25827                             b.updateElement(tb.selectedNode);
25828                             editorcore.syncValue();
25829                             return;
25830                         }
25831                         
25832                         tb.selectedNode.setAttribute(f.attrname, nv);
25833                         editorcore.syncValue();
25834                     }
25835                 }
25836             }));
25837              
25838         }
25839         
25840         var _this = this;
25841         
25842         if(nm == 'BODY'){
25843             tb.addSeparator();
25844         
25845             tb.addButton( {
25846                 text: 'Stylesheets',
25847
25848                 listeners : {
25849                     click : function ()
25850                     {
25851                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
25852                     }
25853                 }
25854             });
25855         }
25856         
25857         tb.addFill();
25858         tb.addButton({
25859             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
25860     
25861             listeners : {
25862                 click : function ()
25863                 {
25864                     // remove
25865                     // undo does not work.
25866                     var sn = tb.selectedNode;
25867                     if (!sn) {
25868                         return;
25869                     }
25870                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
25871                     if (sn.hasAttribute('data-block')) {
25872                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
25873                         sn.parentNode.removeChild(sn);
25874                         
25875                     } else if (sn && sn.tagName != 'BODY') {
25876                         // remove and keep parents.
25877                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
25878                         a.removeTag(sn);
25879                     }
25880                     
25881                     
25882                     var range = editorcore.createRange();
25883         
25884                     range.setStart(stn,0);
25885                     range.setEnd(stn,0); 
25886                     var selection = editorcore.getSelection();
25887                     selection.removeAllRanges();
25888                     selection.addRange(range);
25889                     
25890                     
25891                     //_this.updateToolbar(null, null, pn);
25892                     _this.updateToolbar(null, null, null);
25893                     _this.updateFooter(false);
25894                     
25895                 }
25896             }
25897             
25898                     
25899                 
25900             
25901         });
25902         
25903         
25904         tb.el.on('click', function(e){
25905             e.preventDefault(); // what does this do?
25906         });
25907         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25908         tb.el.hide();
25909         
25910         // dont need to disable them... as they will get hidden
25911         return tb;
25912          
25913         
25914     },
25915     buildFooter : function()
25916     {
25917         
25918         var fel = this.editor.wrap.createChild();
25919         this.footer = new Roo.Toolbar(fel);
25920         // toolbar has scrolly on left / right?
25921         var footDisp= new Roo.Toolbar.Fill();
25922         var _t = this;
25923         this.footer.add(
25924             {
25925                 text : '&lt;',
25926                 xtype: 'Button',
25927                 handler : function() {
25928                     _t.footDisp.scrollTo('left',0,true)
25929                 }
25930             }
25931         );
25932         this.footer.add( footDisp );
25933         this.footer.add( 
25934             {
25935                 text : '&gt;',
25936                 xtype: 'Button',
25937                 handler : function() {
25938                     // no animation..
25939                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
25940                 }
25941             }
25942         );
25943         var fel = Roo.get(footDisp.el);
25944         fel.addClass('x-editor-context');
25945         this.footDispWrap = fel; 
25946         this.footDispWrap.overflow  = 'hidden';
25947         
25948         this.footDisp = fel.createChild();
25949         this.footDispWrap.on('click', this.onContextClick, this)
25950         
25951         
25952     },
25953     // when the footer contect changes
25954     onContextClick : function (ev,dom)
25955     {
25956         ev.preventDefault();
25957         var  cn = dom.className;
25958         //Roo.log(cn);
25959         if (!cn.match(/x-ed-loc-/)) {
25960             return;
25961         }
25962         var n = cn.split('-').pop();
25963         var ans = this.footerEls;
25964         var sel = ans[n];
25965         
25966          // pick
25967         var range = this.editorcore.createRange();
25968         
25969         range.selectNodeContents(sel);
25970         //range.selectNode(sel);
25971         
25972         
25973         var selection = this.editorcore.getSelection();
25974         selection.removeAllRanges();
25975         selection.addRange(range);
25976         
25977         
25978         
25979         this.updateToolbar(null, null, sel);
25980         
25981         
25982     }
25983     
25984     
25985     
25986     
25987     
25988 });
25989
25990
25991
25992
25993
25994 /*
25995  * Based on:
25996  * Ext JS Library 1.1.1
25997  * Copyright(c) 2006-2007, Ext JS, LLC.
25998  *
25999  * Originally Released Under LGPL - original licence link has changed is not relivant.
26000  *
26001  * Fork - LGPL
26002  * <script type="text/javascript">
26003  */
26004  
26005 /**
26006  * @class Roo.form.BasicForm
26007  * @extends Roo.util.Observable
26008  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26009  * @constructor
26010  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26011  * @param {Object} config Configuration options
26012  */
26013 Roo.form.BasicForm = function(el, config){
26014     this.allItems = [];
26015     this.childForms = [];
26016     Roo.apply(this, config);
26017     /*
26018      * The Roo.form.Field items in this form.
26019      * @type MixedCollection
26020      */
26021      
26022      
26023     this.items = new Roo.util.MixedCollection(false, function(o){
26024         return o.id || (o.id = Roo.id());
26025     });
26026     this.addEvents({
26027         /**
26028          * @event beforeaction
26029          * Fires before any action is performed. Return false to cancel the action.
26030          * @param {Form} this
26031          * @param {Action} action The action to be performed
26032          */
26033         beforeaction: true,
26034         /**
26035          * @event actionfailed
26036          * Fires when an action fails.
26037          * @param {Form} this
26038          * @param {Action} action The action that failed
26039          */
26040         actionfailed : true,
26041         /**
26042          * @event actioncomplete
26043          * Fires when an action is completed.
26044          * @param {Form} this
26045          * @param {Action} action The action that completed
26046          */
26047         actioncomplete : true
26048     });
26049     if(el){
26050         this.initEl(el);
26051     }
26052     Roo.form.BasicForm.superclass.constructor.call(this);
26053     
26054     Roo.form.BasicForm.popover.apply();
26055 };
26056
26057 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26058     /**
26059      * @cfg {String} method
26060      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26061      */
26062     /**
26063      * @cfg {DataReader} reader
26064      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26065      * This is optional as there is built-in support for processing JSON.
26066      */
26067     /**
26068      * @cfg {DataReader} errorReader
26069      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26070      * This is completely optional as there is built-in support for processing JSON.
26071      */
26072     /**
26073      * @cfg {String} url
26074      * The URL to use for form actions if one isn't supplied in the action options.
26075      */
26076     /**
26077      * @cfg {Boolean} fileUpload
26078      * Set to true if this form is a file upload.
26079      */
26080      
26081     /**
26082      * @cfg {Object} baseParams
26083      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26084      */
26085      /**
26086      
26087     /**
26088      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26089      */
26090     timeout: 30,
26091
26092     // private
26093     activeAction : null,
26094
26095     /**
26096      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26097      * or setValues() data instead of when the form was first created.
26098      */
26099     trackResetOnLoad : false,
26100     
26101     
26102     /**
26103      * childForms - used for multi-tab forms
26104      * @type {Array}
26105      */
26106     childForms : false,
26107     
26108     /**
26109      * allItems - full list of fields.
26110      * @type {Array}
26111      */
26112     allItems : false,
26113     
26114     /**
26115      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26116      * element by passing it or its id or mask the form itself by passing in true.
26117      * @type Mixed
26118      */
26119     waitMsgTarget : false,
26120     
26121     /**
26122      * @type Boolean
26123      */
26124     disableMask : false,
26125     
26126     /**
26127      * @cfg {Boolean} errorMask (true|false) default false
26128      */
26129     errorMask : false,
26130     
26131     /**
26132      * @cfg {Number} maskOffset Default 100
26133      */
26134     maskOffset : 100,
26135
26136     // private
26137     initEl : function(el){
26138         this.el = Roo.get(el);
26139         this.id = this.el.id || Roo.id();
26140         this.el.on('submit', this.onSubmit, this);
26141         this.el.addClass('x-form');
26142     },
26143
26144     // private
26145     onSubmit : function(e){
26146         e.stopEvent();
26147     },
26148
26149     /**
26150      * Returns true if client-side validation on the form is successful.
26151      * @return Boolean
26152      */
26153     isValid : function(){
26154         var valid = true;
26155         var target = false;
26156         this.items.each(function(f){
26157             if(f.validate()){
26158                 return;
26159             }
26160             
26161             valid = false;
26162                 
26163             if(!target && f.el.isVisible(true)){
26164                 target = f;
26165             }
26166         });
26167         
26168         if(this.errorMask && !valid){
26169             Roo.form.BasicForm.popover.mask(this, target);
26170         }
26171         
26172         return valid;
26173     },
26174     /**
26175      * Returns array of invalid form fields.
26176      * @return Array
26177      */
26178     
26179     invalidFields : function()
26180     {
26181         var ret = [];
26182         this.items.each(function(f){
26183             if(f.validate()){
26184                 return;
26185             }
26186             ret.push(f);
26187             
26188         });
26189         
26190         return ret;
26191     },
26192     
26193     
26194     /**
26195      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
26196      * @return Boolean
26197      */
26198     isDirty : function(){
26199         var dirty = false;
26200         this.items.each(function(f){
26201            if(f.isDirty()){
26202                dirty = true;
26203                return false;
26204            }
26205         });
26206         return dirty;
26207     },
26208     
26209     /**
26210      * Returns true if any fields in this form have changed since their original load. (New version)
26211      * @return Boolean
26212      */
26213     
26214     hasChanged : function()
26215     {
26216         var dirty = false;
26217         this.items.each(function(f){
26218            if(f.hasChanged()){
26219                dirty = true;
26220                return false;
26221            }
26222         });
26223         return dirty;
26224         
26225     },
26226     /**
26227      * Resets all hasChanged to 'false' -
26228      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
26229      * So hasChanged storage is only to be used for this purpose
26230      * @return Boolean
26231      */
26232     resetHasChanged : function()
26233     {
26234         this.items.each(function(f){
26235            f.resetHasChanged();
26236         });
26237         
26238     },
26239     
26240     
26241     /**
26242      * Performs a predefined action (submit or load) or custom actions you define on this form.
26243      * @param {String} actionName The name of the action type
26244      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26245      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26246      * accept other config options):
26247      * <pre>
26248 Property          Type             Description
26249 ----------------  ---------------  ----------------------------------------------------------------------------------
26250 url               String           The url for the action (defaults to the form's url)
26251 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26252 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26253 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26254                                    validate the form on the client (defaults to false)
26255      * </pre>
26256      * @return {BasicForm} this
26257      */
26258     doAction : function(action, options){
26259         if(typeof action == 'string'){
26260             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26261         }
26262         if(this.fireEvent('beforeaction', this, action) !== false){
26263             this.beforeAction(action);
26264             action.run.defer(100, action);
26265         }
26266         return this;
26267     },
26268
26269     /**
26270      * Shortcut to do a submit action.
26271      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26272      * @return {BasicForm} this
26273      */
26274     submit : function(options){
26275         this.doAction('submit', options);
26276         return this;
26277     },
26278
26279     /**
26280      * Shortcut to do a load action.
26281      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26282      * @return {BasicForm} this
26283      */
26284     load : function(options){
26285         this.doAction('load', options);
26286         return this;
26287     },
26288
26289     /**
26290      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26291      * @param {Record} record The record to edit
26292      * @return {BasicForm} this
26293      */
26294     updateRecord : function(record){
26295         record.beginEdit();
26296         var fs = record.fields;
26297         fs.each(function(f){
26298             var field = this.findField(f.name);
26299             if(field){
26300                 record.set(f.name, field.getValue());
26301             }
26302         }, this);
26303         record.endEdit();
26304         return this;
26305     },
26306
26307     /**
26308      * Loads an Roo.data.Record into this form.
26309      * @param {Record} record The record to load
26310      * @return {BasicForm} this
26311      */
26312     loadRecord : function(record){
26313         this.setValues(record.data);
26314         return this;
26315     },
26316
26317     // private
26318     beforeAction : function(action){
26319         var o = action.options;
26320         
26321         if(!this.disableMask) {
26322             if(this.waitMsgTarget === true){
26323                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26324             }else if(this.waitMsgTarget){
26325                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26326                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26327             }else {
26328                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26329             }
26330         }
26331         
26332          
26333     },
26334
26335     // private
26336     afterAction : function(action, success){
26337         this.activeAction = null;
26338         var o = action.options;
26339         
26340         if(!this.disableMask) {
26341             if(this.waitMsgTarget === true){
26342                 this.el.unmask();
26343             }else if(this.waitMsgTarget){
26344                 this.waitMsgTarget.unmask();
26345             }else{
26346                 Roo.MessageBox.updateProgress(1);
26347                 Roo.MessageBox.hide();
26348             }
26349         }
26350         
26351         if(success){
26352             if(o.reset){
26353                 this.reset();
26354             }
26355             Roo.callback(o.success, o.scope, [this, action]);
26356             this.fireEvent('actioncomplete', this, action);
26357             
26358         }else{
26359             
26360             // failure condition..
26361             // we have a scenario where updates need confirming.
26362             // eg. if a locking scenario exists..
26363             // we look for { errors : { needs_confirm : true }} in the response.
26364             if (
26365                 (typeof(action.result) != 'undefined')  &&
26366                 (typeof(action.result.errors) != 'undefined')  &&
26367                 (typeof(action.result.errors.needs_confirm) != 'undefined')
26368            ){
26369                 var _t = this;
26370                 Roo.MessageBox.confirm(
26371                     "Change requires confirmation",
26372                     action.result.errorMsg,
26373                     function(r) {
26374                         if (r != 'yes') {
26375                             return;
26376                         }
26377                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
26378                     }
26379                     
26380                 );
26381                 
26382                 
26383                 
26384                 return;
26385             }
26386             
26387             Roo.callback(o.failure, o.scope, [this, action]);
26388             // show an error message if no failed handler is set..
26389             if (!this.hasListener('actionfailed')) {
26390                 Roo.MessageBox.alert("Error",
26391                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
26392                         action.result.errorMsg :
26393                         "Saving Failed, please check your entries or try again"
26394                 );
26395             }
26396             
26397             this.fireEvent('actionfailed', this, action);
26398         }
26399         
26400     },
26401
26402     /**
26403      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26404      * @param {String} id The value to search for
26405      * @return Field
26406      */
26407     findField : function(id){
26408         var field = this.items.get(id);
26409         if(!field){
26410             this.items.each(function(f){
26411                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26412                     field = f;
26413                     return false;
26414                 }
26415             });
26416         }
26417         return field || null;
26418     },
26419
26420     /**
26421      * Add a secondary form to this one, 
26422      * Used to provide tabbed forms. One form is primary, with hidden values 
26423      * which mirror the elements from the other forms.
26424      * 
26425      * @param {Roo.form.Form} form to add.
26426      * 
26427      */
26428     addForm : function(form)
26429     {
26430        
26431         if (this.childForms.indexOf(form) > -1) {
26432             // already added..
26433             return;
26434         }
26435         this.childForms.push(form);
26436         var n = '';
26437         Roo.each(form.allItems, function (fe) {
26438             
26439             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26440             if (this.findField(n)) { // already added..
26441                 return;
26442             }
26443             var add = new Roo.form.Hidden({
26444                 name : n
26445             });
26446             add.render(this.el);
26447             
26448             this.add( add );
26449         }, this);
26450         
26451     },
26452     /**
26453      * Mark fields in this form invalid in bulk.
26454      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26455      * @return {BasicForm} this
26456      */
26457     markInvalid : function(errors){
26458         if(errors instanceof Array){
26459             for(var i = 0, len = errors.length; i < len; i++){
26460                 var fieldError = errors[i];
26461                 var f = this.findField(fieldError.id);
26462                 if(f){
26463                     f.markInvalid(fieldError.msg);
26464                 }
26465             }
26466         }else{
26467             var field, id;
26468             for(id in errors){
26469                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26470                     field.markInvalid(errors[id]);
26471                 }
26472             }
26473         }
26474         Roo.each(this.childForms || [], function (f) {
26475             f.markInvalid(errors);
26476         });
26477         
26478         return this;
26479     },
26480
26481     /**
26482      * Set values for fields in this form in bulk.
26483      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26484      * @return {BasicForm} this
26485      */
26486     setValues : function(values){
26487         if(values instanceof Array){ // array of objects
26488             for(var i = 0, len = values.length; i < len; i++){
26489                 var v = values[i];
26490                 var f = this.findField(v.id);
26491                 if(f){
26492                     f.setValue(v.value);
26493                     if(this.trackResetOnLoad){
26494                         f.originalValue = f.getValue();
26495                     }
26496                 }
26497             }
26498         }else{ // object hash
26499             var field, id;
26500             for(id in values){
26501                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26502                     
26503                     if (field.setFromData && 
26504                         field.valueField && 
26505                         field.displayField &&
26506                         // combos' with local stores can 
26507                         // be queried via setValue()
26508                         // to set their value..
26509                         (field.store && !field.store.isLocal)
26510                         ) {
26511                         // it's a combo
26512                         var sd = { };
26513                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26514                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26515                         field.setFromData(sd);
26516                         
26517                     } else {
26518                         field.setValue(values[id]);
26519                     }
26520                     
26521                     
26522                     if(this.trackResetOnLoad){
26523                         field.originalValue = field.getValue();
26524                     }
26525                 }
26526             }
26527         }
26528         this.resetHasChanged();
26529         
26530         
26531         Roo.each(this.childForms || [], function (f) {
26532             f.setValues(values);
26533             f.resetHasChanged();
26534         });
26535                 
26536         return this;
26537     },
26538  
26539     /**
26540      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26541      * they are returned as an array.
26542      * @param {Boolean} asString
26543      * @return {Object}
26544      */
26545     getValues : function(asString){
26546         if (this.childForms) {
26547             // copy values from the child forms
26548             Roo.each(this.childForms, function (f) {
26549                 this.setValues(f.getValues());
26550             }, this);
26551         }
26552         
26553         // use formdata
26554         if (typeof(FormData) != 'undefined' && asString !== true) {
26555             // this relies on a 'recent' version of chrome apparently...
26556             try {
26557                 var fd = (new FormData(this.el.dom)).entries();
26558                 var ret = {};
26559                 var ent = fd.next();
26560                 while (!ent.done) {
26561                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
26562                     ent = fd.next();
26563                 };
26564                 return ret;
26565             } catch(e) {
26566                 
26567             }
26568             
26569         }
26570         
26571         
26572         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26573         if(asString === true){
26574             return fs;
26575         }
26576         return Roo.urlDecode(fs);
26577     },
26578     
26579     /**
26580      * Returns the fields in this form as an object with key/value pairs. 
26581      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26582      * @return {Object}
26583      */
26584     getFieldValues : function(with_hidden)
26585     {
26586         if (this.childForms) {
26587             // copy values from the child forms
26588             // should this call getFieldValues - probably not as we do not currently copy
26589             // hidden fields when we generate..
26590             Roo.each(this.childForms, function (f) {
26591                 this.setValues(f.getValues());
26592             }, this);
26593         }
26594         
26595         var ret = {};
26596         this.items.each(function(f){
26597             if (!f.getName()) {
26598                 return;
26599             }
26600             var v = f.getValue();
26601             if (f.inputType =='radio') {
26602                 if (typeof(ret[f.getName()]) == 'undefined') {
26603                     ret[f.getName()] = ''; // empty..
26604                 }
26605                 
26606                 if (!f.el.dom.checked) {
26607                     return;
26608                     
26609                 }
26610                 v = f.el.dom.value;
26611                 
26612             }
26613             
26614             // not sure if this supported any more..
26615             if ((typeof(v) == 'object') && f.getRawValue) {
26616                 v = f.getRawValue() ; // dates..
26617             }
26618             // combo boxes where name != hiddenName...
26619             if (f.name != f.getName()) {
26620                 ret[f.name] = f.getRawValue();
26621             }
26622             ret[f.getName()] = v;
26623         });
26624         
26625         return ret;
26626     },
26627
26628     /**
26629      * Clears all invalid messages in this form.
26630      * @return {BasicForm} this
26631      */
26632     clearInvalid : function(){
26633         this.items.each(function(f){
26634            f.clearInvalid();
26635         });
26636         
26637         Roo.each(this.childForms || [], function (f) {
26638             f.clearInvalid();
26639         });
26640         
26641         
26642         return this;
26643     },
26644
26645     /**
26646      * Resets this form.
26647      * @return {BasicForm} this
26648      */
26649     reset : function(){
26650         this.items.each(function(f){
26651             f.reset();
26652         });
26653         
26654         Roo.each(this.childForms || [], function (f) {
26655             f.reset();
26656         });
26657         this.resetHasChanged();
26658         
26659         return this;
26660     },
26661
26662     /**
26663      * Add Roo.form components to this form.
26664      * @param {Field} field1
26665      * @param {Field} field2 (optional)
26666      * @param {Field} etc (optional)
26667      * @return {BasicForm} this
26668      */
26669     add : function(){
26670         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26671         return this;
26672     },
26673
26674
26675     /**
26676      * Removes a field from the items collection (does NOT remove its markup).
26677      * @param {Field} field
26678      * @return {BasicForm} this
26679      */
26680     remove : function(field){
26681         this.items.remove(field);
26682         return this;
26683     },
26684
26685     /**
26686      * Looks at the fields in this form, checks them for an id attribute,
26687      * and calls applyTo on the existing dom element with that id.
26688      * @return {BasicForm} this
26689      */
26690     render : function(){
26691         this.items.each(function(f){
26692             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26693                 f.applyTo(f.id);
26694             }
26695         });
26696         return this;
26697     },
26698
26699     /**
26700      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26701      * @param {Object} values
26702      * @return {BasicForm} this
26703      */
26704     applyToFields : function(o){
26705         this.items.each(function(f){
26706            Roo.apply(f, o);
26707         });
26708         return this;
26709     },
26710
26711     /**
26712      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26713      * @param {Object} values
26714      * @return {BasicForm} this
26715      */
26716     applyIfToFields : function(o){
26717         this.items.each(function(f){
26718            Roo.applyIf(f, o);
26719         });
26720         return this;
26721     }
26722 });
26723
26724 // back compat
26725 Roo.BasicForm = Roo.form.BasicForm;
26726
26727 Roo.apply(Roo.form.BasicForm, {
26728     
26729     popover : {
26730         
26731         padding : 5,
26732         
26733         isApplied : false,
26734         
26735         isMasked : false,
26736         
26737         form : false,
26738         
26739         target : false,
26740         
26741         intervalID : false,
26742         
26743         maskEl : false,
26744         
26745         apply : function()
26746         {
26747             if(this.isApplied){
26748                 return;
26749             }
26750             
26751             this.maskEl = {
26752                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
26753                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
26754                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
26755                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
26756             };
26757             
26758             this.maskEl.top.enableDisplayMode("block");
26759             this.maskEl.left.enableDisplayMode("block");
26760             this.maskEl.bottom.enableDisplayMode("block");
26761             this.maskEl.right.enableDisplayMode("block");
26762             
26763             Roo.get(document.body).on('click', function(){
26764                 this.unmask();
26765             }, this);
26766             
26767             Roo.get(document.body).on('touchstart', function(){
26768                 this.unmask();
26769             }, this);
26770             
26771             this.isApplied = true
26772         },
26773         
26774         mask : function(form, target)
26775         {
26776             this.form = form;
26777             
26778             this.target = target;
26779             
26780             if(!this.form.errorMask || !target.el){
26781                 return;
26782             }
26783             
26784             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
26785             
26786             var ot = this.target.el.calcOffsetsTo(scrollable);
26787             
26788             var scrollTo = ot[1] - this.form.maskOffset;
26789             
26790             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
26791             
26792             scrollable.scrollTo('top', scrollTo);
26793             
26794             var el = this.target.wrap || this.target.el;
26795             
26796             var box = el.getBox();
26797             
26798             this.maskEl.top.setStyle('position', 'absolute');
26799             this.maskEl.top.setStyle('z-index', 10000);
26800             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
26801             this.maskEl.top.setLeft(0);
26802             this.maskEl.top.setTop(0);
26803             this.maskEl.top.show();
26804             
26805             this.maskEl.left.setStyle('position', 'absolute');
26806             this.maskEl.left.setStyle('z-index', 10000);
26807             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
26808             this.maskEl.left.setLeft(0);
26809             this.maskEl.left.setTop(box.y - this.padding);
26810             this.maskEl.left.show();
26811
26812             this.maskEl.bottom.setStyle('position', 'absolute');
26813             this.maskEl.bottom.setStyle('z-index', 10000);
26814             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
26815             this.maskEl.bottom.setLeft(0);
26816             this.maskEl.bottom.setTop(box.bottom + this.padding);
26817             this.maskEl.bottom.show();
26818
26819             this.maskEl.right.setStyle('position', 'absolute');
26820             this.maskEl.right.setStyle('z-index', 10000);
26821             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
26822             this.maskEl.right.setLeft(box.right + this.padding);
26823             this.maskEl.right.setTop(box.y - this.padding);
26824             this.maskEl.right.show();
26825
26826             this.intervalID = window.setInterval(function() {
26827                 Roo.form.BasicForm.popover.unmask();
26828             }, 10000);
26829
26830             window.onwheel = function(){ return false;};
26831             
26832             (function(){ this.isMasked = true; }).defer(500, this);
26833             
26834         },
26835         
26836         unmask : function()
26837         {
26838             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
26839                 return;
26840             }
26841             
26842             this.maskEl.top.setStyle('position', 'absolute');
26843             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
26844             this.maskEl.top.hide();
26845
26846             this.maskEl.left.setStyle('position', 'absolute');
26847             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
26848             this.maskEl.left.hide();
26849
26850             this.maskEl.bottom.setStyle('position', 'absolute');
26851             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
26852             this.maskEl.bottom.hide();
26853
26854             this.maskEl.right.setStyle('position', 'absolute');
26855             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
26856             this.maskEl.right.hide();
26857             
26858             window.onwheel = function(){ return true;};
26859             
26860             if(this.intervalID){
26861                 window.clearInterval(this.intervalID);
26862                 this.intervalID = false;
26863             }
26864             
26865             this.isMasked = false;
26866             
26867         }
26868         
26869     }
26870     
26871 });/*
26872  * Based on:
26873  * Ext JS Library 1.1.1
26874  * Copyright(c) 2006-2007, Ext JS, LLC.
26875  *
26876  * Originally Released Under LGPL - original licence link has changed is not relivant.
26877  *
26878  * Fork - LGPL
26879  * <script type="text/javascript">
26880  */
26881
26882 /**
26883  * @class Roo.form.Form
26884  * @extends Roo.form.BasicForm
26885  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26886  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26887  * @constructor
26888  * @param {Object} config Configuration options
26889  */
26890 Roo.form.Form = function(config){
26891     var xitems =  [];
26892     if (config.items) {
26893         xitems = config.items;
26894         delete config.items;
26895     }
26896    
26897     
26898     Roo.form.Form.superclass.constructor.call(this, null, config);
26899     this.url = this.url || this.action;
26900     if(!this.root){
26901         this.root = new Roo.form.Layout(Roo.applyIf({
26902             id: Roo.id()
26903         }, config));
26904     }
26905     this.active = this.root;
26906     /**
26907      * Array of all the buttons that have been added to this form via {@link addButton}
26908      * @type Array
26909      */
26910     this.buttons = [];
26911     this.allItems = [];
26912     this.addEvents({
26913         /**
26914          * @event clientvalidation
26915          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26916          * @param {Form} this
26917          * @param {Boolean} valid true if the form has passed client-side validation
26918          */
26919         clientvalidation: true,
26920         /**
26921          * @event rendered
26922          * Fires when the form is rendered
26923          * @param {Roo.form.Form} form
26924          */
26925         rendered : true
26926     });
26927     
26928     if (this.progressUrl) {
26929             // push a hidden field onto the list of fields..
26930             this.addxtype( {
26931                     xns: Roo.form, 
26932                     xtype : 'Hidden', 
26933                     name : 'UPLOAD_IDENTIFIER' 
26934             });
26935         }
26936         
26937     
26938     Roo.each(xitems, this.addxtype, this);
26939     
26940 };
26941
26942 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26943      /**
26944      * @cfg {Roo.Button} buttons[] buttons at bottom of form
26945      */
26946     
26947     /**
26948      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26949      */
26950     /**
26951      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26952      */
26953     /**
26954      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26955      */
26956     buttonAlign:'center',
26957
26958     /**
26959      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26960      */
26961     minButtonWidth:75,
26962
26963     /**
26964      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26965      * This property cascades to child containers if not set.
26966      */
26967     labelAlign:'left',
26968
26969     /**
26970      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26971      * fires a looping event with that state. This is required to bind buttons to the valid
26972      * state using the config value formBind:true on the button.
26973      */
26974     monitorValid : false,
26975
26976     /**
26977      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26978      */
26979     monitorPoll : 200,
26980     
26981     /**
26982      * @cfg {String} progressUrl - Url to return progress data 
26983      */
26984     
26985     progressUrl : false,
26986     /**
26987      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
26988      * sending a formdata with extra parameters - eg uploaded elements.
26989      */
26990     
26991     formData : false,
26992     
26993     /**
26994      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26995      * fields are added and the column is closed. If no fields are passed the column remains open
26996      * until end() is called.
26997      * @param {Object} config The config to pass to the column
26998      * @param {Field} field1 (optional)
26999      * @param {Field} field2 (optional)
27000      * @param {Field} etc (optional)
27001      * @return Column The column container object
27002      */
27003     column : function(c){
27004         var col = new Roo.form.Column(c);
27005         this.start(col);
27006         if(arguments.length > 1){ // duplicate code required because of Opera
27007             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27008             this.end();
27009         }
27010         return col;
27011     },
27012
27013     /**
27014      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27015      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27016      * until end() is called.
27017      * @param {Object} config The config to pass to the fieldset
27018      * @param {Field} field1 (optional)
27019      * @param {Field} field2 (optional)
27020      * @param {Field} etc (optional)
27021      * @return FieldSet The fieldset container object
27022      */
27023     fieldset : function(c){
27024         var fs = new Roo.form.FieldSet(c);
27025         this.start(fs);
27026         if(arguments.length > 1){ // duplicate code required because of Opera
27027             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27028             this.end();
27029         }
27030         return fs;
27031     },
27032
27033     /**
27034      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27035      * fields are added and the container is closed. If no fields are passed the container remains open
27036      * until end() is called.
27037      * @param {Object} config The config to pass to the Layout
27038      * @param {Field} field1 (optional)
27039      * @param {Field} field2 (optional)
27040      * @param {Field} etc (optional)
27041      * @return Layout The container object
27042      */
27043     container : function(c){
27044         var l = new Roo.form.Layout(c);
27045         this.start(l);
27046         if(arguments.length > 1){ // duplicate code required because of Opera
27047             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27048             this.end();
27049         }
27050         return l;
27051     },
27052
27053     /**
27054      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27055      * @param {Object} container A Roo.form.Layout or subclass of Layout
27056      * @return {Form} this
27057      */
27058     start : function(c){
27059         // cascade label info
27060         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27061         this.active.stack.push(c);
27062         c.ownerCt = this.active;
27063         this.active = c;
27064         return this;
27065     },
27066
27067     /**
27068      * Closes the current open container
27069      * @return {Form} this
27070      */
27071     end : function(){
27072         if(this.active == this.root){
27073             return this;
27074         }
27075         this.active = this.active.ownerCt;
27076         return this;
27077     },
27078
27079     /**
27080      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27081      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27082      * as the label of the field.
27083      * @param {Field} field1
27084      * @param {Field} field2 (optional)
27085      * @param {Field} etc. (optional)
27086      * @return {Form} this
27087      */
27088     add : function(){
27089         this.active.stack.push.apply(this.active.stack, arguments);
27090         this.allItems.push.apply(this.allItems,arguments);
27091         var r = [];
27092         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27093             if(a[i].isFormField){
27094                 r.push(a[i]);
27095             }
27096         }
27097         if(r.length > 0){
27098             Roo.form.Form.superclass.add.apply(this, r);
27099         }
27100         return this;
27101     },
27102     
27103
27104     
27105     
27106     
27107      /**
27108      * Find any element that has been added to a form, using it's ID or name
27109      * This can include framesets, columns etc. along with regular fields..
27110      * @param {String} id - id or name to find.
27111      
27112      * @return {Element} e - or false if nothing found.
27113      */
27114     findbyId : function(id)
27115     {
27116         var ret = false;
27117         if (!id) {
27118             return ret;
27119         }
27120         Roo.each(this.allItems, function(f){
27121             if (f.id == id || f.name == id ){
27122                 ret = f;
27123                 return false;
27124             }
27125         });
27126         return ret;
27127     },
27128
27129     
27130     
27131     /**
27132      * Render this form into the passed container. This should only be called once!
27133      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27134      * @return {Form} this
27135      */
27136     render : function(ct)
27137     {
27138         
27139         
27140         
27141         ct = Roo.get(ct);
27142         var o = this.autoCreate || {
27143             tag: 'form',
27144             method : this.method || 'POST',
27145             id : this.id || Roo.id()
27146         };
27147         this.initEl(ct.createChild(o));
27148
27149         this.root.render(this.el);
27150         
27151        
27152              
27153         this.items.each(function(f){
27154             f.render('x-form-el-'+f.id);
27155         });
27156
27157         if(this.buttons.length > 0){
27158             // tables are required to maintain order and for correct IE layout
27159             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27160                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27161                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27162             }}, null, true);
27163             var tr = tb.getElementsByTagName('tr')[0];
27164             for(var i = 0, len = this.buttons.length; i < len; i++) {
27165                 var b = this.buttons[i];
27166                 var td = document.createElement('td');
27167                 td.className = 'x-form-btn-td';
27168                 b.render(tr.appendChild(td));
27169             }
27170         }
27171         if(this.monitorValid){ // initialize after render
27172             this.startMonitoring();
27173         }
27174         this.fireEvent('rendered', this);
27175         return this;
27176     },
27177
27178     /**
27179      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27180      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27181      * object or a valid Roo.DomHelper element config
27182      * @param {Function} handler The function called when the button is clicked
27183      * @param {Object} scope (optional) The scope of the handler function
27184      * @return {Roo.Button}
27185      */
27186     addButton : function(config, handler, scope){
27187         var bc = {
27188             handler: handler,
27189             scope: scope,
27190             minWidth: this.minButtonWidth,
27191             hideParent:true
27192         };
27193         if(typeof config == "string"){
27194             bc.text = config;
27195         }else{
27196             Roo.apply(bc, config);
27197         }
27198         var btn = new Roo.Button(null, bc);
27199         this.buttons.push(btn);
27200         return btn;
27201     },
27202
27203      /**
27204      * Adds a series of form elements (using the xtype property as the factory method.
27205      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27206      * @param {Object} config 
27207      */
27208     
27209     addxtype : function()
27210     {
27211         var ar = Array.prototype.slice.call(arguments, 0);
27212         var ret = false;
27213         for(var i = 0; i < ar.length; i++) {
27214             if (!ar[i]) {
27215                 continue; // skip -- if this happends something invalid got sent, we 
27216                 // should ignore it, as basically that interface element will not show up
27217                 // and that should be pretty obvious!!
27218             }
27219             
27220             if (Roo.form[ar[i].xtype]) {
27221                 ar[i].form = this;
27222                 var fe = Roo.factory(ar[i], Roo.form);
27223                 if (!ret) {
27224                     ret = fe;
27225                 }
27226                 fe.form = this;
27227                 if (fe.store) {
27228                     fe.store.form = this;
27229                 }
27230                 if (fe.isLayout) {  
27231                          
27232                     this.start(fe);
27233                     this.allItems.push(fe);
27234                     if (fe.items && fe.addxtype) {
27235                         fe.addxtype.apply(fe, fe.items);
27236                         delete fe.items;
27237                     }
27238                      this.end();
27239                     continue;
27240                 }
27241                 
27242                 
27243                  
27244                 this.add(fe);
27245               //  console.log('adding ' + ar[i].xtype);
27246             }
27247             if (ar[i].xtype == 'Button') {  
27248                 //console.log('adding button');
27249                 //console.log(ar[i]);
27250                 this.addButton(ar[i]);
27251                 this.allItems.push(fe);
27252                 continue;
27253             }
27254             
27255             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27256                 alert('end is not supported on xtype any more, use items');
27257             //    this.end();
27258             //    //console.log('adding end');
27259             }
27260             
27261         }
27262         return ret;
27263     },
27264     
27265     /**
27266      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27267      * option "monitorValid"
27268      */
27269     startMonitoring : function(){
27270         if(!this.bound){
27271             this.bound = true;
27272             Roo.TaskMgr.start({
27273                 run : this.bindHandler,
27274                 interval : this.monitorPoll || 200,
27275                 scope: this
27276             });
27277         }
27278     },
27279
27280     /**
27281      * Stops monitoring of the valid state of this form
27282      */
27283     stopMonitoring : function(){
27284         this.bound = false;
27285     },
27286
27287     // private
27288     bindHandler : function(){
27289         if(!this.bound){
27290             return false; // stops binding
27291         }
27292         var valid = true;
27293         this.items.each(function(f){
27294             if(!f.isValid(true)){
27295                 valid = false;
27296                 return false;
27297             }
27298         });
27299         for(var i = 0, len = this.buttons.length; i < len; i++){
27300             var btn = this.buttons[i];
27301             if(btn.formBind === true && btn.disabled === valid){
27302                 btn.setDisabled(!valid);
27303             }
27304         }
27305         this.fireEvent('clientvalidation', this, valid);
27306     }
27307     
27308     
27309     
27310     
27311     
27312     
27313     
27314     
27315 });
27316
27317
27318 // back compat
27319 Roo.Form = Roo.form.Form;
27320 /*
27321  * Based on:
27322  * Ext JS Library 1.1.1
27323  * Copyright(c) 2006-2007, Ext JS, LLC.
27324  *
27325  * Originally Released Under LGPL - original licence link has changed is not relivant.
27326  *
27327  * Fork - LGPL
27328  * <script type="text/javascript">
27329  */
27330
27331 // as we use this in bootstrap.
27332 Roo.namespace('Roo.form');
27333  /**
27334  * @class Roo.form.Action
27335  * Internal Class used to handle form actions
27336  * @constructor
27337  * @param {Roo.form.BasicForm} el The form element or its id
27338  * @param {Object} config Configuration options
27339  */
27340
27341  
27342  
27343 // define the action interface
27344 Roo.form.Action = function(form, options){
27345     this.form = form;
27346     this.options = options || {};
27347 };
27348 /**
27349  * Client Validation Failed
27350  * @const 
27351  */
27352 Roo.form.Action.CLIENT_INVALID = 'client';
27353 /**
27354  * Server Validation Failed
27355  * @const 
27356  */
27357 Roo.form.Action.SERVER_INVALID = 'server';
27358  /**
27359  * Connect to Server Failed
27360  * @const 
27361  */
27362 Roo.form.Action.CONNECT_FAILURE = 'connect';
27363 /**
27364  * Reading Data from Server Failed
27365  * @const 
27366  */
27367 Roo.form.Action.LOAD_FAILURE = 'load';
27368
27369 Roo.form.Action.prototype = {
27370     type : 'default',
27371     failureType : undefined,
27372     response : undefined,
27373     result : undefined,
27374
27375     // interface method
27376     run : function(options){
27377
27378     },
27379
27380     // interface method
27381     success : function(response){
27382
27383     },
27384
27385     // interface method
27386     handleResponse : function(response){
27387
27388     },
27389
27390     // default connection failure
27391     failure : function(response){
27392         
27393         this.response = response;
27394         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27395         this.form.afterAction(this, false);
27396     },
27397
27398     processResponse : function(response){
27399         this.response = response;
27400         if(!response.responseText){
27401             return true;
27402         }
27403         this.result = this.handleResponse(response);
27404         return this.result;
27405     },
27406
27407     // utility functions used internally
27408     getUrl : function(appendParams){
27409         var url = this.options.url || this.form.url || this.form.el.dom.action;
27410         if(appendParams){
27411             var p = this.getParams();
27412             if(p){
27413                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27414             }
27415         }
27416         return url;
27417     },
27418
27419     getMethod : function(){
27420         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27421     },
27422
27423     getParams : function(){
27424         var bp = this.form.baseParams;
27425         var p = this.options.params;
27426         if(p){
27427             if(typeof p == "object"){
27428                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27429             }else if(typeof p == 'string' && bp){
27430                 p += '&' + Roo.urlEncode(bp);
27431             }
27432         }else if(bp){
27433             p = Roo.urlEncode(bp);
27434         }
27435         return p;
27436     },
27437
27438     createCallback : function(){
27439         return {
27440             success: this.success,
27441             failure: this.failure,
27442             scope: this,
27443             timeout: (this.form.timeout*1000),
27444             upload: this.form.fileUpload ? this.success : undefined
27445         };
27446     }
27447 };
27448
27449 Roo.form.Action.Submit = function(form, options){
27450     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27451 };
27452
27453 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27454     type : 'submit',
27455
27456     haveProgress : false,
27457     uploadComplete : false,
27458     
27459     // uploadProgress indicator.
27460     uploadProgress : function()
27461     {
27462         if (!this.form.progressUrl) {
27463             return;
27464         }
27465         
27466         if (!this.haveProgress) {
27467             Roo.MessageBox.progress("Uploading", "Uploading");
27468         }
27469         if (this.uploadComplete) {
27470            Roo.MessageBox.hide();
27471            return;
27472         }
27473         
27474         this.haveProgress = true;
27475    
27476         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27477         
27478         var c = new Roo.data.Connection();
27479         c.request({
27480             url : this.form.progressUrl,
27481             params: {
27482                 id : uid
27483             },
27484             method: 'GET',
27485             success : function(req){
27486                //console.log(data);
27487                 var rdata = false;
27488                 var edata;
27489                 try  {
27490                    rdata = Roo.decode(req.responseText)
27491                 } catch (e) {
27492                     Roo.log("Invalid data from server..");
27493                     Roo.log(edata);
27494                     return;
27495                 }
27496                 if (!rdata || !rdata.success) {
27497                     Roo.log(rdata);
27498                     Roo.MessageBox.alert(Roo.encode(rdata));
27499                     return;
27500                 }
27501                 var data = rdata.data;
27502                 
27503                 if (this.uploadComplete) {
27504                    Roo.MessageBox.hide();
27505                    return;
27506                 }
27507                    
27508                 if (data){
27509                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27510                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27511                     );
27512                 }
27513                 this.uploadProgress.defer(2000,this);
27514             },
27515        
27516             failure: function(data) {
27517                 Roo.log('progress url failed ');
27518                 Roo.log(data);
27519             },
27520             scope : this
27521         });
27522            
27523     },
27524     
27525     
27526     run : function()
27527     {
27528         // run get Values on the form, so it syncs any secondary forms.
27529         this.form.getValues();
27530         
27531         var o = this.options;
27532         var method = this.getMethod();
27533         var isPost = method == 'POST';
27534         if(o.clientValidation === false || this.form.isValid()){
27535             
27536             if (this.form.progressUrl) {
27537                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27538                     (new Date() * 1) + '' + Math.random());
27539                     
27540             } 
27541             
27542             
27543             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27544                 form:this.form.el.dom,
27545                 url:this.getUrl(!isPost),
27546                 method: method,
27547                 params:isPost ? this.getParams() : null,
27548                 isUpload: this.form.fileUpload,
27549                 formData : this.form.formData
27550             }));
27551             
27552             this.uploadProgress();
27553
27554         }else if (o.clientValidation !== false){ // client validation failed
27555             this.failureType = Roo.form.Action.CLIENT_INVALID;
27556             this.form.afterAction(this, false);
27557         }
27558     },
27559
27560     success : function(response)
27561     {
27562         this.uploadComplete= true;
27563         if (this.haveProgress) {
27564             Roo.MessageBox.hide();
27565         }
27566         
27567         
27568         var result = this.processResponse(response);
27569         if(result === true || result.success){
27570             this.form.afterAction(this, true);
27571             return;
27572         }
27573         if(result.errors){
27574             this.form.markInvalid(result.errors);
27575             this.failureType = Roo.form.Action.SERVER_INVALID;
27576         }
27577         this.form.afterAction(this, false);
27578     },
27579     failure : function(response)
27580     {
27581         this.uploadComplete= true;
27582         if (this.haveProgress) {
27583             Roo.MessageBox.hide();
27584         }
27585         
27586         this.response = response;
27587         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27588         this.form.afterAction(this, false);
27589     },
27590     
27591     handleResponse : function(response){
27592         if(this.form.errorReader){
27593             var rs = this.form.errorReader.read(response);
27594             var errors = [];
27595             if(rs.records){
27596                 for(var i = 0, len = rs.records.length; i < len; i++) {
27597                     var r = rs.records[i];
27598                     errors[i] = r.data;
27599                 }
27600             }
27601             if(errors.length < 1){
27602                 errors = null;
27603             }
27604             return {
27605                 success : rs.success,
27606                 errors : errors
27607             };
27608         }
27609         var ret = false;
27610         try {
27611             ret = Roo.decode(response.responseText);
27612         } catch (e) {
27613             ret = {
27614                 success: false,
27615                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27616                 errors : []
27617             };
27618         }
27619         return ret;
27620         
27621     }
27622 });
27623
27624
27625 Roo.form.Action.Load = function(form, options){
27626     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27627     this.reader = this.form.reader;
27628 };
27629
27630 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27631     type : 'load',
27632
27633     run : function(){
27634         
27635         Roo.Ajax.request(Roo.apply(
27636                 this.createCallback(), {
27637                     method:this.getMethod(),
27638                     url:this.getUrl(false),
27639                     params:this.getParams()
27640         }));
27641     },
27642
27643     success : function(response){
27644         
27645         var result = this.processResponse(response);
27646         if(result === true || !result.success || !result.data){
27647             this.failureType = Roo.form.Action.LOAD_FAILURE;
27648             this.form.afterAction(this, false);
27649             return;
27650         }
27651         this.form.clearInvalid();
27652         this.form.setValues(result.data);
27653         this.form.afterAction(this, true);
27654     },
27655
27656     handleResponse : function(response){
27657         if(this.form.reader){
27658             var rs = this.form.reader.read(response);
27659             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27660             return {
27661                 success : rs.success,
27662                 data : data
27663             };
27664         }
27665         return Roo.decode(response.responseText);
27666     }
27667 });
27668
27669 Roo.form.Action.ACTION_TYPES = {
27670     'load' : Roo.form.Action.Load,
27671     'submit' : Roo.form.Action.Submit
27672 };/*
27673  * Based on:
27674  * Ext JS Library 1.1.1
27675  * Copyright(c) 2006-2007, Ext JS, LLC.
27676  *
27677  * Originally Released Under LGPL - original licence link has changed is not relivant.
27678  *
27679  * Fork - LGPL
27680  * <script type="text/javascript">
27681  */
27682  
27683 /**
27684  * @class Roo.form.Layout
27685  * @extends Roo.Component
27686  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27687  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27688  * @constructor
27689  * @param {Object} config Configuration options
27690  */
27691 Roo.form.Layout = function(config){
27692     var xitems = [];
27693     if (config.items) {
27694         xitems = config.items;
27695         delete config.items;
27696     }
27697     Roo.form.Layout.superclass.constructor.call(this, config);
27698     this.stack = [];
27699     Roo.each(xitems, this.addxtype, this);
27700      
27701 };
27702
27703 Roo.extend(Roo.form.Layout, Roo.Component, {
27704     /**
27705      * @cfg {String/Object} autoCreate
27706      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27707      */
27708     /**
27709      * @cfg {String/Object/Function} style
27710      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27711      * a function which returns such a specification.
27712      */
27713     /**
27714      * @cfg {String} labelAlign
27715      * Valid values are "left," "top" and "right" (defaults to "left")
27716      */
27717     /**
27718      * @cfg {Number} labelWidth
27719      * Fixed width in pixels of all field labels (defaults to undefined)
27720      */
27721     /**
27722      * @cfg {Boolean} clear
27723      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27724      */
27725     clear : true,
27726     /**
27727      * @cfg {String} labelSeparator
27728      * The separator to use after field labels (defaults to ':')
27729      */
27730     labelSeparator : ':',
27731     /**
27732      * @cfg {Boolean} hideLabels
27733      * True to suppress the display of field labels in this layout (defaults to false)
27734      */
27735     hideLabels : false,
27736
27737     // private
27738     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27739     
27740     isLayout : true,
27741     
27742     // private
27743     onRender : function(ct, position){
27744         if(this.el){ // from markup
27745             this.el = Roo.get(this.el);
27746         }else {  // generate
27747             var cfg = this.getAutoCreate();
27748             this.el = ct.createChild(cfg, position);
27749         }
27750         if(this.style){
27751             this.el.applyStyles(this.style);
27752         }
27753         if(this.labelAlign){
27754             this.el.addClass('x-form-label-'+this.labelAlign);
27755         }
27756         if(this.hideLabels){
27757             this.labelStyle = "display:none";
27758             this.elementStyle = "padding-left:0;";
27759         }else{
27760             if(typeof this.labelWidth == 'number'){
27761                 this.labelStyle = "width:"+this.labelWidth+"px;";
27762                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27763             }
27764             if(this.labelAlign == 'top'){
27765                 this.labelStyle = "width:auto;";
27766                 this.elementStyle = "padding-left:0;";
27767             }
27768         }
27769         var stack = this.stack;
27770         var slen = stack.length;
27771         if(slen > 0){
27772             if(!this.fieldTpl){
27773                 var t = new Roo.Template(
27774                     '<div class="x-form-item {5}">',
27775                         '<label for="{0}" style="{2}">{1}{4}</label>',
27776                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27777                         '</div>',
27778                     '</div><div class="x-form-clear-left"></div>'
27779                 );
27780                 t.disableFormats = true;
27781                 t.compile();
27782                 Roo.form.Layout.prototype.fieldTpl = t;
27783             }
27784             for(var i = 0; i < slen; i++) {
27785                 if(stack[i].isFormField){
27786                     this.renderField(stack[i]);
27787                 }else{
27788                     this.renderComponent(stack[i]);
27789                 }
27790             }
27791         }
27792         if(this.clear){
27793             this.el.createChild({cls:'x-form-clear'});
27794         }
27795     },
27796
27797     // private
27798     renderField : function(f){
27799         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27800                f.id, //0
27801                f.fieldLabel, //1
27802                f.labelStyle||this.labelStyle||'', //2
27803                this.elementStyle||'', //3
27804                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27805                f.itemCls||this.itemCls||''  //5
27806        ], true).getPrevSibling());
27807     },
27808
27809     // private
27810     renderComponent : function(c){
27811         c.render(c.isLayout ? this.el : this.el.createChild());    
27812     },
27813     /**
27814      * Adds a object form elements (using the xtype property as the factory method.)
27815      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27816      * @param {Object} config 
27817      */
27818     addxtype : function(o)
27819     {
27820         // create the lement.
27821         o.form = this.form;
27822         var fe = Roo.factory(o, Roo.form);
27823         this.form.allItems.push(fe);
27824         this.stack.push(fe);
27825         
27826         if (fe.isFormField) {
27827             this.form.items.add(fe);
27828         }
27829          
27830         return fe;
27831     }
27832 });
27833
27834 /**
27835  * @class Roo.form.Column
27836  * @extends Roo.form.Layout
27837  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27838  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27839  * @constructor
27840  * @param {Object} config Configuration options
27841  */
27842 Roo.form.Column = function(config){
27843     Roo.form.Column.superclass.constructor.call(this, config);
27844 };
27845
27846 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27847     /**
27848      * @cfg {Number/String} width
27849      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27850      */
27851     /**
27852      * @cfg {String/Object} autoCreate
27853      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27854      */
27855
27856     // private
27857     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27858
27859     // private
27860     onRender : function(ct, position){
27861         Roo.form.Column.superclass.onRender.call(this, ct, position);
27862         if(this.width){
27863             this.el.setWidth(this.width);
27864         }
27865     }
27866 });
27867
27868
27869 /**
27870  * @class Roo.form.Row
27871  * @extends Roo.form.Layout
27872  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27873  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27874  * @constructor
27875  * @param {Object} config Configuration options
27876  */
27877
27878  
27879 Roo.form.Row = function(config){
27880     Roo.form.Row.superclass.constructor.call(this, config);
27881 };
27882  
27883 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27884       /**
27885      * @cfg {Number/String} width
27886      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27887      */
27888     /**
27889      * @cfg {Number/String} height
27890      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27891      */
27892     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27893     
27894     padWidth : 20,
27895     // private
27896     onRender : function(ct, position){
27897         //console.log('row render');
27898         if(!this.rowTpl){
27899             var t = new Roo.Template(
27900                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27901                     '<label for="{0}" style="{2}">{1}{4}</label>',
27902                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27903                     '</div>',
27904                 '</div>'
27905             );
27906             t.disableFormats = true;
27907             t.compile();
27908             Roo.form.Layout.prototype.rowTpl = t;
27909         }
27910         this.fieldTpl = this.rowTpl;
27911         
27912         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27913         var labelWidth = 100;
27914         
27915         if ((this.labelAlign != 'top')) {
27916             if (typeof this.labelWidth == 'number') {
27917                 labelWidth = this.labelWidth
27918             }
27919             this.padWidth =  20 + labelWidth;
27920             
27921         }
27922         
27923         Roo.form.Column.superclass.onRender.call(this, ct, position);
27924         if(this.width){
27925             this.el.setWidth(this.width);
27926         }
27927         if(this.height){
27928             this.el.setHeight(this.height);
27929         }
27930     },
27931     
27932     // private
27933     renderField : function(f){
27934         f.fieldEl = this.fieldTpl.append(this.el, [
27935                f.id, f.fieldLabel,
27936                f.labelStyle||this.labelStyle||'',
27937                this.elementStyle||'',
27938                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27939                f.itemCls||this.itemCls||'',
27940                f.width ? f.width + this.padWidth : 160 + this.padWidth
27941        ],true);
27942     }
27943 });
27944  
27945
27946 /**
27947  * @class Roo.form.FieldSet
27948  * @extends Roo.form.Layout
27949  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27950  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27951  * @constructor
27952  * @param {Object} config Configuration options
27953  */
27954 Roo.form.FieldSet = function(config){
27955     Roo.form.FieldSet.superclass.constructor.call(this, config);
27956 };
27957
27958 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27959     /**
27960      * @cfg {String} legend
27961      * The text to display as the legend for the FieldSet (defaults to '')
27962      */
27963     /**
27964      * @cfg {String/Object} autoCreate
27965      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27966      */
27967
27968     // private
27969     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27970
27971     // private
27972     onRender : function(ct, position){
27973         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27974         if(this.legend){
27975             this.setLegend(this.legend);
27976         }
27977     },
27978
27979     // private
27980     setLegend : function(text){
27981         if(this.rendered){
27982             this.el.child('legend').update(text);
27983         }
27984     }
27985 });/*
27986  * Based on:
27987  * Ext JS Library 1.1.1
27988  * Copyright(c) 2006-2007, Ext JS, LLC.
27989  *
27990  * Originally Released Under LGPL - original licence link has changed is not relivant.
27991  *
27992  * Fork - LGPL
27993  * <script type="text/javascript">
27994  */
27995 /**
27996  * @class Roo.form.VTypes
27997  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27998  * @static
27999  */
28000 Roo.form.VTypes = function(){
28001     // closure these in so they are only created once.
28002     var alpha = /^[a-zA-Z_]+$/;
28003     var alphanum = /^[a-zA-Z0-9_]+$/;
28004     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
28005     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28006
28007     // All these messages and functions are configurable
28008     return {
28009         /**
28010          * The function used to validate email addresses
28011          * @param {String} value The email address
28012          */
28013         'email' : function(v){
28014             return email.test(v);
28015         },
28016         /**
28017          * The error text to display when the email validation function returns false
28018          * @type String
28019          */
28020         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28021         /**
28022          * The keystroke filter mask to be applied on email input
28023          * @type RegExp
28024          */
28025         'emailMask' : /[a-z0-9_\.\-@]/i,
28026
28027         /**
28028          * The function used to validate URLs
28029          * @param {String} value The URL
28030          */
28031         'url' : function(v){
28032             return url.test(v);
28033         },
28034         /**
28035          * The error text to display when the url validation function returns false
28036          * @type String
28037          */
28038         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28039         
28040         /**
28041          * The function used to validate alpha values
28042          * @param {String} value The value
28043          */
28044         'alpha' : function(v){
28045             return alpha.test(v);
28046         },
28047         /**
28048          * The error text to display when the alpha validation function returns false
28049          * @type String
28050          */
28051         'alphaText' : 'This field should only contain letters and _',
28052         /**
28053          * The keystroke filter mask to be applied on alpha input
28054          * @type RegExp
28055          */
28056         'alphaMask' : /[a-z_]/i,
28057
28058         /**
28059          * The function used to validate alphanumeric values
28060          * @param {String} value The value
28061          */
28062         'alphanum' : function(v){
28063             return alphanum.test(v);
28064         },
28065         /**
28066          * The error text to display when the alphanumeric validation function returns false
28067          * @type String
28068          */
28069         'alphanumText' : 'This field should only contain letters, numbers and _',
28070         /**
28071          * The keystroke filter mask to be applied on alphanumeric input
28072          * @type RegExp
28073          */
28074         'alphanumMask' : /[a-z0-9_]/i
28075     };
28076 }();//<script type="text/javascript">
28077
28078 /**
28079  * @class Roo.form.FCKeditor
28080  * @extends Roo.form.TextArea
28081  * Wrapper around the FCKEditor http://www.fckeditor.net
28082  * @constructor
28083  * Creates a new FCKeditor
28084  * @param {Object} config Configuration options
28085  */
28086 Roo.form.FCKeditor = function(config){
28087     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28088     this.addEvents({
28089          /**
28090          * @event editorinit
28091          * Fired when the editor is initialized - you can add extra handlers here..
28092          * @param {FCKeditor} this
28093          * @param {Object} the FCK object.
28094          */
28095         editorinit : true
28096     });
28097     
28098     
28099 };
28100 Roo.form.FCKeditor.editors = { };
28101 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28102 {
28103     //defaultAutoCreate : {
28104     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28105     //},
28106     // private
28107     /**
28108      * @cfg {Object} fck options - see fck manual for details.
28109      */
28110     fckconfig : false,
28111     
28112     /**
28113      * @cfg {Object} fck toolbar set (Basic or Default)
28114      */
28115     toolbarSet : 'Basic',
28116     /**
28117      * @cfg {Object} fck BasePath
28118      */ 
28119     basePath : '/fckeditor/',
28120     
28121     
28122     frame : false,
28123     
28124     value : '',
28125     
28126    
28127     onRender : function(ct, position)
28128     {
28129         if(!this.el){
28130             this.defaultAutoCreate = {
28131                 tag: "textarea",
28132                 style:"width:300px;height:60px;",
28133                 autocomplete: "new-password"
28134             };
28135         }
28136         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28137         /*
28138         if(this.grow){
28139             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28140             if(this.preventScrollbars){
28141                 this.el.setStyle("overflow", "hidden");
28142             }
28143             this.el.setHeight(this.growMin);
28144         }
28145         */
28146         //console.log('onrender' + this.getId() );
28147         Roo.form.FCKeditor.editors[this.getId()] = this;
28148          
28149
28150         this.replaceTextarea() ;
28151         
28152     },
28153     
28154     getEditor : function() {
28155         return this.fckEditor;
28156     },
28157     /**
28158      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28159      * @param {Mixed} value The value to set
28160      */
28161     
28162     
28163     setValue : function(value)
28164     {
28165         //console.log('setValue: ' + value);
28166         
28167         if(typeof(value) == 'undefined') { // not sure why this is happending...
28168             return;
28169         }
28170         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28171         
28172         //if(!this.el || !this.getEditor()) {
28173         //    this.value = value;
28174             //this.setValue.defer(100,this,[value]);    
28175         //    return;
28176         //} 
28177         
28178         if(!this.getEditor()) {
28179             return;
28180         }
28181         
28182         this.getEditor().SetData(value);
28183         
28184         //
28185
28186     },
28187
28188     /**
28189      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28190      * @return {Mixed} value The field value
28191      */
28192     getValue : function()
28193     {
28194         
28195         if (this.frame && this.frame.dom.style.display == 'none') {
28196             return Roo.form.FCKeditor.superclass.getValue.call(this);
28197         }
28198         
28199         if(!this.el || !this.getEditor()) {
28200            
28201            // this.getValue.defer(100,this); 
28202             return this.value;
28203         }
28204        
28205         
28206         var value=this.getEditor().GetData();
28207         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28208         return Roo.form.FCKeditor.superclass.getValue.call(this);
28209         
28210
28211     },
28212
28213     /**
28214      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28215      * @return {Mixed} value The field value
28216      */
28217     getRawValue : function()
28218     {
28219         if (this.frame && this.frame.dom.style.display == 'none') {
28220             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28221         }
28222         
28223         if(!this.el || !this.getEditor()) {
28224             //this.getRawValue.defer(100,this); 
28225             return this.value;
28226             return;
28227         }
28228         
28229         
28230         
28231         var value=this.getEditor().GetData();
28232         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28233         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28234          
28235     },
28236     
28237     setSize : function(w,h) {
28238         
28239         
28240         
28241         //if (this.frame && this.frame.dom.style.display == 'none') {
28242         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28243         //    return;
28244         //}
28245         //if(!this.el || !this.getEditor()) {
28246         //    this.setSize.defer(100,this, [w,h]); 
28247         //    return;
28248         //}
28249         
28250         
28251         
28252         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28253         
28254         this.frame.dom.setAttribute('width', w);
28255         this.frame.dom.setAttribute('height', h);
28256         this.frame.setSize(w,h);
28257         
28258     },
28259     
28260     toggleSourceEdit : function(value) {
28261         
28262       
28263          
28264         this.el.dom.style.display = value ? '' : 'none';
28265         this.frame.dom.style.display = value ?  'none' : '';
28266         
28267     },
28268     
28269     
28270     focus: function(tag)
28271     {
28272         if (this.frame.dom.style.display == 'none') {
28273             return Roo.form.FCKeditor.superclass.focus.call(this);
28274         }
28275         if(!this.el || !this.getEditor()) {
28276             this.focus.defer(100,this, [tag]); 
28277             return;
28278         }
28279         
28280         
28281         
28282         
28283         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28284         this.getEditor().Focus();
28285         if (tgs.length) {
28286             if (!this.getEditor().Selection.GetSelection()) {
28287                 this.focus.defer(100,this, [tag]); 
28288                 return;
28289             }
28290             
28291             
28292             var r = this.getEditor().EditorDocument.createRange();
28293             r.setStart(tgs[0],0);
28294             r.setEnd(tgs[0],0);
28295             this.getEditor().Selection.GetSelection().removeAllRanges();
28296             this.getEditor().Selection.GetSelection().addRange(r);
28297             this.getEditor().Focus();
28298         }
28299         
28300     },
28301     
28302     
28303     
28304     replaceTextarea : function()
28305     {
28306         if ( document.getElementById( this.getId() + '___Frame' ) ) {
28307             return ;
28308         }
28309         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28310         //{
28311             // We must check the elements firstly using the Id and then the name.
28312         var oTextarea = document.getElementById( this.getId() );
28313         
28314         var colElementsByName = document.getElementsByName( this.getId() ) ;
28315          
28316         oTextarea.style.display = 'none' ;
28317
28318         if ( oTextarea.tabIndex ) {            
28319             this.TabIndex = oTextarea.tabIndex ;
28320         }
28321         
28322         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28323         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28324         this.frame = Roo.get(this.getId() + '___Frame')
28325     },
28326     
28327     _getConfigHtml : function()
28328     {
28329         var sConfig = '' ;
28330
28331         for ( var o in this.fckconfig ) {
28332             sConfig += sConfig.length > 0  ? '&amp;' : '';
28333             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28334         }
28335
28336         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28337     },
28338     
28339     
28340     _getIFrameHtml : function()
28341     {
28342         var sFile = 'fckeditor.html' ;
28343         /* no idea what this is about..
28344         try
28345         {
28346             if ( (/fcksource=true/i).test( window.top.location.search ) )
28347                 sFile = 'fckeditor.original.html' ;
28348         }
28349         catch (e) { 
28350         */
28351
28352         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28353         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28354         
28355         
28356         var html = '<iframe id="' + this.getId() +
28357             '___Frame" src="' + sLink +
28358             '" width="' + this.width +
28359             '" height="' + this.height + '"' +
28360             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28361             ' frameborder="0" scrolling="no"></iframe>' ;
28362
28363         return html ;
28364     },
28365     
28366     _insertHtmlBefore : function( html, element )
28367     {
28368         if ( element.insertAdjacentHTML )       {
28369             // IE
28370             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28371         } else { // Gecko
28372             var oRange = document.createRange() ;
28373             oRange.setStartBefore( element ) ;
28374             var oFragment = oRange.createContextualFragment( html );
28375             element.parentNode.insertBefore( oFragment, element ) ;
28376         }
28377     }
28378     
28379     
28380   
28381     
28382     
28383     
28384     
28385
28386 });
28387
28388 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28389
28390 function FCKeditor_OnComplete(editorInstance){
28391     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28392     f.fckEditor = editorInstance;
28393     //console.log("loaded");
28394     f.fireEvent('editorinit', f, editorInstance);
28395
28396   
28397
28398  
28399
28400
28401
28402
28403
28404
28405
28406
28407
28408
28409
28410
28411
28412
28413
28414 //<script type="text/javascript">
28415 /**
28416  * @class Roo.form.GridField
28417  * @extends Roo.form.Field
28418  * Embed a grid (or editable grid into a form)
28419  * STATUS ALPHA
28420  * 
28421  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28422  * it needs 
28423  * xgrid.store = Roo.data.Store
28424  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28425  * xgrid.store.reader = Roo.data.JsonReader 
28426  * 
28427  * 
28428  * @constructor
28429  * Creates a new GridField
28430  * @param {Object} config Configuration options
28431  */
28432 Roo.form.GridField = function(config){
28433     Roo.form.GridField.superclass.constructor.call(this, config);
28434      
28435 };
28436
28437 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28438     /**
28439      * @cfg {Number} width  - used to restrict width of grid..
28440      */
28441     width : 100,
28442     /**
28443      * @cfg {Number} height - used to restrict height of grid..
28444      */
28445     height : 50,
28446      /**
28447      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28448          * 
28449          *}
28450      */
28451     xgrid : false, 
28452     /**
28453      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28454      * {tag: "input", type: "checkbox", autocomplete: "off"})
28455      */
28456    // defaultAutoCreate : { tag: 'div' },
28457     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
28458     /**
28459      * @cfg {String} addTitle Text to include for adding a title.
28460      */
28461     addTitle : false,
28462     //
28463     onResize : function(){
28464         Roo.form.Field.superclass.onResize.apply(this, arguments);
28465     },
28466
28467     initEvents : function(){
28468         // Roo.form.Checkbox.superclass.initEvents.call(this);
28469         // has no events...
28470        
28471     },
28472
28473
28474     getResizeEl : function(){
28475         return this.wrap;
28476     },
28477
28478     getPositionEl : function(){
28479         return this.wrap;
28480     },
28481
28482     // private
28483     onRender : function(ct, position){
28484         
28485         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28486         var style = this.style;
28487         delete this.style;
28488         
28489         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28490         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28491         this.viewEl = this.wrap.createChild({ tag: 'div' });
28492         if (style) {
28493             this.viewEl.applyStyles(style);
28494         }
28495         if (this.width) {
28496             this.viewEl.setWidth(this.width);
28497         }
28498         if (this.height) {
28499             this.viewEl.setHeight(this.height);
28500         }
28501         //if(this.inputValue !== undefined){
28502         //this.setValue(this.value);
28503         
28504         
28505         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28506         
28507         
28508         this.grid.render();
28509         this.grid.getDataSource().on('remove', this.refreshValue, this);
28510         this.grid.getDataSource().on('update', this.refreshValue, this);
28511         this.grid.on('afteredit', this.refreshValue, this);
28512  
28513     },
28514      
28515     
28516     /**
28517      * Sets the value of the item. 
28518      * @param {String} either an object  or a string..
28519      */
28520     setValue : function(v){
28521         //this.value = v;
28522         v = v || []; // empty set..
28523         // this does not seem smart - it really only affects memoryproxy grids..
28524         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28525             var ds = this.grid.getDataSource();
28526             // assumes a json reader..
28527             var data = {}
28528             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28529             ds.loadData( data);
28530         }
28531         // clear selection so it does not get stale.
28532         if (this.grid.sm) { 
28533             this.grid.sm.clearSelections();
28534         }
28535         
28536         Roo.form.GridField.superclass.setValue.call(this, v);
28537         this.refreshValue();
28538         // should load data in the grid really....
28539     },
28540     
28541     // private
28542     refreshValue: function() {
28543          var val = [];
28544         this.grid.getDataSource().each(function(r) {
28545             val.push(r.data);
28546         });
28547         this.el.dom.value = Roo.encode(val);
28548     }
28549     
28550      
28551     
28552     
28553 });/*
28554  * Based on:
28555  * Ext JS Library 1.1.1
28556  * Copyright(c) 2006-2007, Ext JS, LLC.
28557  *
28558  * Originally Released Under LGPL - original licence link has changed is not relivant.
28559  *
28560  * Fork - LGPL
28561  * <script type="text/javascript">
28562  */
28563 /**
28564  * @class Roo.form.DisplayField
28565  * @extends Roo.form.Field
28566  * A generic Field to display non-editable data.
28567  * @cfg {Boolean} closable (true|false) default false
28568  * @constructor
28569  * Creates a new Display Field item.
28570  * @param {Object} config Configuration options
28571  */
28572 Roo.form.DisplayField = function(config){
28573     Roo.form.DisplayField.superclass.constructor.call(this, config);
28574     
28575     this.addEvents({
28576         /**
28577          * @event close
28578          * Fires after the click the close btn
28579              * @param {Roo.form.DisplayField} this
28580              */
28581         close : true
28582     });
28583 };
28584
28585 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28586     inputType:      'hidden',
28587     allowBlank:     true,
28588     readOnly:         true,
28589     
28590  
28591     /**
28592      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28593      */
28594     focusClass : undefined,
28595     /**
28596      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28597      */
28598     fieldClass: 'x-form-field',
28599     
28600      /**
28601      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28602      */
28603     valueRenderer: undefined,
28604     
28605     width: 100,
28606     /**
28607      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28608      * {tag: "input", type: "checkbox", autocomplete: "off"})
28609      */
28610      
28611  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28612  
28613     closable : false,
28614     
28615     onResize : function(){
28616         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28617         
28618     },
28619
28620     initEvents : function(){
28621         // Roo.form.Checkbox.superclass.initEvents.call(this);
28622         // has no events...
28623         
28624         if(this.closable){
28625             this.closeEl.on('click', this.onClose, this);
28626         }
28627        
28628     },
28629
28630
28631     getResizeEl : function(){
28632         return this.wrap;
28633     },
28634
28635     getPositionEl : function(){
28636         return this.wrap;
28637     },
28638
28639     // private
28640     onRender : function(ct, position){
28641         
28642         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28643         //if(this.inputValue !== undefined){
28644         this.wrap = this.el.wrap();
28645         
28646         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28647         
28648         if(this.closable){
28649             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
28650         }
28651         
28652         if (this.bodyStyle) {
28653             this.viewEl.applyStyles(this.bodyStyle);
28654         }
28655         //this.viewEl.setStyle('padding', '2px');
28656         
28657         this.setValue(this.value);
28658         
28659     },
28660 /*
28661     // private
28662     initValue : Roo.emptyFn,
28663
28664   */
28665
28666         // private
28667     onClick : function(){
28668         
28669     },
28670
28671     /**
28672      * Sets the checked state of the checkbox.
28673      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28674      */
28675     setValue : function(v){
28676         this.value = v;
28677         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28678         // this might be called before we have a dom element..
28679         if (!this.viewEl) {
28680             return;
28681         }
28682         this.viewEl.dom.innerHTML = html;
28683         Roo.form.DisplayField.superclass.setValue.call(this, v);
28684
28685     },
28686     
28687     onClose : function(e)
28688     {
28689         e.preventDefault();
28690         
28691         this.fireEvent('close', this);
28692     }
28693 });/*
28694  * 
28695  * Licence- LGPL
28696  * 
28697  */
28698
28699 /**
28700  * @class Roo.form.DayPicker
28701  * @extends Roo.form.Field
28702  * A Day picker show [M] [T] [W] ....
28703  * @constructor
28704  * Creates a new Day Picker
28705  * @param {Object} config Configuration options
28706  */
28707 Roo.form.DayPicker= function(config){
28708     Roo.form.DayPicker.superclass.constructor.call(this, config);
28709      
28710 };
28711
28712 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28713     /**
28714      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28715      */
28716     focusClass : undefined,
28717     /**
28718      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28719      */
28720     fieldClass: "x-form-field",
28721    
28722     /**
28723      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28724      * {tag: "input", type: "checkbox", autocomplete: "off"})
28725      */
28726     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
28727     
28728    
28729     actionMode : 'viewEl', 
28730     //
28731     // private
28732  
28733     inputType : 'hidden',
28734     
28735      
28736     inputElement: false, // real input element?
28737     basedOn: false, // ????
28738     
28739     isFormField: true, // not sure where this is needed!!!!
28740
28741     onResize : function(){
28742         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
28743         if(!this.boxLabel){
28744             this.el.alignTo(this.wrap, 'c-c');
28745         }
28746     },
28747
28748     initEvents : function(){
28749         Roo.form.Checkbox.superclass.initEvents.call(this);
28750         this.el.on("click", this.onClick,  this);
28751         this.el.on("change", this.onClick,  this);
28752     },
28753
28754
28755     getResizeEl : function(){
28756         return this.wrap;
28757     },
28758
28759     getPositionEl : function(){
28760         return this.wrap;
28761     },
28762
28763     
28764     // private
28765     onRender : function(ct, position){
28766         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
28767        
28768         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
28769         
28770         var r1 = '<table><tr>';
28771         var r2 = '<tr class="x-form-daypick-icons">';
28772         for (var i=0; i < 7; i++) {
28773             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
28774             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
28775         }
28776         
28777         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
28778         viewEl.select('img').on('click', this.onClick, this);
28779         this.viewEl = viewEl;   
28780         
28781         
28782         // this will not work on Chrome!!!
28783         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
28784         this.el.on('propertychange', this.setFromHidden,  this);  //ie
28785         
28786         
28787           
28788
28789     },
28790
28791     // private
28792     initValue : Roo.emptyFn,
28793
28794     /**
28795      * Returns the checked state of the checkbox.
28796      * @return {Boolean} True if checked, else false
28797      */
28798     getValue : function(){
28799         return this.el.dom.value;
28800         
28801     },
28802
28803         // private
28804     onClick : function(e){ 
28805         //this.setChecked(!this.checked);
28806         Roo.get(e.target).toggleClass('x-menu-item-checked');
28807         this.refreshValue();
28808         //if(this.el.dom.checked != this.checked){
28809         //    this.setValue(this.el.dom.checked);
28810        // }
28811     },
28812     
28813     // private
28814     refreshValue : function()
28815     {
28816         var val = '';
28817         this.viewEl.select('img',true).each(function(e,i,n)  {
28818             val += e.is(".x-menu-item-checked") ? String(n) : '';
28819         });
28820         this.setValue(val, true);
28821     },
28822
28823     /**
28824      * Sets the checked state of the checkbox.
28825      * On is always based on a string comparison between inputValue and the param.
28826      * @param {Boolean/String} value - the value to set 
28827      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
28828      */
28829     setValue : function(v,suppressEvent){
28830         if (!this.el.dom) {
28831             return;
28832         }
28833         var old = this.el.dom.value ;
28834         this.el.dom.value = v;
28835         if (suppressEvent) {
28836             return ;
28837         }
28838          
28839         // update display..
28840         this.viewEl.select('img',true).each(function(e,i,n)  {
28841             
28842             var on = e.is(".x-menu-item-checked");
28843             var newv = v.indexOf(String(n)) > -1;
28844             if (on != newv) {
28845                 e.toggleClass('x-menu-item-checked');
28846             }
28847             
28848         });
28849         
28850         
28851         this.fireEvent('change', this, v, old);
28852         
28853         
28854     },
28855    
28856     // handle setting of hidden value by some other method!!?!?
28857     setFromHidden: function()
28858     {
28859         if(!this.el){
28860             return;
28861         }
28862         //console.log("SET FROM HIDDEN");
28863         //alert('setFrom hidden');
28864         this.setValue(this.el.dom.value);
28865     },
28866     
28867     onDestroy : function()
28868     {
28869         if(this.viewEl){
28870             Roo.get(this.viewEl).remove();
28871         }
28872          
28873         Roo.form.DayPicker.superclass.onDestroy.call(this);
28874     }
28875
28876 });/*
28877  * RooJS Library 1.1.1
28878  * Copyright(c) 2008-2011  Alan Knowles
28879  *
28880  * License - LGPL
28881  */
28882  
28883
28884 /**
28885  * @class Roo.form.ComboCheck
28886  * @extends Roo.form.ComboBox
28887  * A combobox for multiple select items.
28888  *
28889  * FIXME - could do with a reset button..
28890  * 
28891  * @constructor
28892  * Create a new ComboCheck
28893  * @param {Object} config Configuration options
28894  */
28895 Roo.form.ComboCheck = function(config){
28896     Roo.form.ComboCheck.superclass.constructor.call(this, config);
28897     // should verify some data...
28898     // like
28899     // hiddenName = required..
28900     // displayField = required
28901     // valudField == required
28902     var req= [ 'hiddenName', 'displayField', 'valueField' ];
28903     var _t = this;
28904     Roo.each(req, function(e) {
28905         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
28906             throw "Roo.form.ComboCheck : missing value for: " + e;
28907         }
28908     });
28909     
28910     
28911 };
28912
28913 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
28914      
28915      
28916     editable : false,
28917      
28918     selectedClass: 'x-menu-item-checked', 
28919     
28920     // private
28921     onRender : function(ct, position){
28922         var _t = this;
28923         
28924         
28925         
28926         if(!this.tpl){
28927             var cls = 'x-combo-list';
28928
28929             
28930             this.tpl =  new Roo.Template({
28931                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
28932                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
28933                    '<span>{' + this.displayField + '}</span>' +
28934                     '</div>' 
28935                 
28936             });
28937         }
28938  
28939         
28940         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
28941         this.view.singleSelect = false;
28942         this.view.multiSelect = true;
28943         this.view.toggleSelect = true;
28944         this.pageTb.add(new Roo.Toolbar.Fill(), {
28945             
28946             text: 'Done',
28947             handler: function()
28948             {
28949                 _t.collapse();
28950             }
28951         });
28952     },
28953     
28954     onViewOver : function(e, t){
28955         // do nothing...
28956         return;
28957         
28958     },
28959     
28960     onViewClick : function(doFocus,index){
28961         return;
28962         
28963     },
28964     select: function () {
28965         //Roo.log("SELECT CALLED");
28966     },
28967      
28968     selectByValue : function(xv, scrollIntoView){
28969         var ar = this.getValueArray();
28970         var sels = [];
28971         
28972         Roo.each(ar, function(v) {
28973             if(v === undefined || v === null){
28974                 return;
28975             }
28976             var r = this.findRecord(this.valueField, v);
28977             if(r){
28978                 sels.push(this.store.indexOf(r))
28979                 
28980             }
28981         },this);
28982         this.view.select(sels);
28983         return false;
28984     },
28985     
28986     
28987     
28988     onSelect : function(record, index){
28989        // Roo.log("onselect Called");
28990        // this is only called by the clear button now..
28991         this.view.clearSelections();
28992         this.setValue('[]');
28993         if (this.value != this.valueBefore) {
28994             this.fireEvent('change', this, this.value, this.valueBefore);
28995             this.valueBefore = this.value;
28996         }
28997     },
28998     getValueArray : function()
28999     {
29000         var ar = [] ;
29001         
29002         try {
29003             //Roo.log(this.value);
29004             if (typeof(this.value) == 'undefined') {
29005                 return [];
29006             }
29007             var ar = Roo.decode(this.value);
29008             return  ar instanceof Array ? ar : []; //?? valid?
29009             
29010         } catch(e) {
29011             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29012             return [];
29013         }
29014          
29015     },
29016     expand : function ()
29017     {
29018         
29019         Roo.form.ComboCheck.superclass.expand.call(this);
29020         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
29021         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
29022         
29023
29024     },
29025     
29026     collapse : function(){
29027         Roo.form.ComboCheck.superclass.collapse.call(this);
29028         var sl = this.view.getSelectedIndexes();
29029         var st = this.store;
29030         var nv = [];
29031         var tv = [];
29032         var r;
29033         Roo.each(sl, function(i) {
29034             r = st.getAt(i);
29035             nv.push(r.get(this.valueField));
29036         },this);
29037         this.setValue(Roo.encode(nv));
29038         if (this.value != this.valueBefore) {
29039
29040             this.fireEvent('change', this, this.value, this.valueBefore);
29041             this.valueBefore = this.value;
29042         }
29043         
29044     },
29045     
29046     setValue : function(v){
29047         // Roo.log(v);
29048         this.value = v;
29049         
29050         var vals = this.getValueArray();
29051         var tv = [];
29052         Roo.each(vals, function(k) {
29053             var r = this.findRecord(this.valueField, k);
29054             if(r){
29055                 tv.push(r.data[this.displayField]);
29056             }else if(this.valueNotFoundText !== undefined){
29057                 tv.push( this.valueNotFoundText );
29058             }
29059         },this);
29060        // Roo.log(tv);
29061         
29062         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29063         this.hiddenField.value = v;
29064         this.value = v;
29065     }
29066     
29067 });/*
29068  * Based on:
29069  * Ext JS Library 1.1.1
29070  * Copyright(c) 2006-2007, Ext JS, LLC.
29071  *
29072  * Originally Released Under LGPL - original licence link has changed is not relivant.
29073  *
29074  * Fork - LGPL
29075  * <script type="text/javascript">
29076  */
29077  
29078 /**
29079  * @class Roo.form.Signature
29080  * @extends Roo.form.Field
29081  * Signature field.  
29082  * @constructor
29083  * 
29084  * @param {Object} config Configuration options
29085  */
29086
29087 Roo.form.Signature = function(config){
29088     Roo.form.Signature.superclass.constructor.call(this, config);
29089     
29090     this.addEvents({// not in used??
29091          /**
29092          * @event confirm
29093          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
29094              * @param {Roo.form.Signature} combo This combo box
29095              */
29096         'confirm' : true,
29097         /**
29098          * @event reset
29099          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
29100              * @param {Roo.form.ComboBox} combo This combo box
29101              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
29102              */
29103         'reset' : true
29104     });
29105 };
29106
29107 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
29108     /**
29109      * @cfg {Object} labels Label to use when rendering a form.
29110      * defaults to 
29111      * labels : { 
29112      *      clear : "Clear",
29113      *      confirm : "Confirm"
29114      *  }
29115      */
29116     labels : { 
29117         clear : "Clear",
29118         confirm : "Confirm"
29119     },
29120     /**
29121      * @cfg {Number} width The signature panel width (defaults to 300)
29122      */
29123     width: 300,
29124     /**
29125      * @cfg {Number} height The signature panel height (defaults to 100)
29126      */
29127     height : 100,
29128     /**
29129      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
29130      */
29131     allowBlank : false,
29132     
29133     //private
29134     // {Object} signPanel The signature SVG panel element (defaults to {})
29135     signPanel : {},
29136     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
29137     isMouseDown : false,
29138     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
29139     isConfirmed : false,
29140     // {String} signatureTmp SVG mapping string (defaults to empty string)
29141     signatureTmp : '',
29142     
29143     
29144     defaultAutoCreate : { // modified by initCompnoent..
29145         tag: "input",
29146         type:"hidden"
29147     },
29148
29149     // private
29150     onRender : function(ct, position){
29151         
29152         Roo.form.Signature.superclass.onRender.call(this, ct, position);
29153         
29154         this.wrap = this.el.wrap({
29155             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
29156         });
29157         
29158         this.createToolbar(this);
29159         this.signPanel = this.wrap.createChild({
29160                 tag: 'div',
29161                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
29162             }, this.el
29163         );
29164             
29165         this.svgID = Roo.id();
29166         this.svgEl = this.signPanel.createChild({
29167               xmlns : 'http://www.w3.org/2000/svg',
29168               tag : 'svg',
29169               id : this.svgID + "-svg",
29170               width: this.width,
29171               height: this.height,
29172               viewBox: '0 0 '+this.width+' '+this.height,
29173               cn : [
29174                 {
29175                     tag: "rect",
29176                     id: this.svgID + "-svg-r",
29177                     width: this.width,
29178                     height: this.height,
29179                     fill: "#ffa"
29180                 },
29181                 {
29182                     tag: "line",
29183                     id: this.svgID + "-svg-l",
29184                     x1: "0", // start
29185                     y1: (this.height*0.8), // start set the line in 80% of height
29186                     x2: this.width, // end
29187                     y2: (this.height*0.8), // end set the line in 80% of height
29188                     'stroke': "#666",
29189                     'stroke-width': "1",
29190                     'stroke-dasharray': "3",
29191                     'shape-rendering': "crispEdges",
29192                     'pointer-events': "none"
29193                 },
29194                 {
29195                     tag: "path",
29196                     id: this.svgID + "-svg-p",
29197                     'stroke': "navy",
29198                     'stroke-width': "3",
29199                     'fill': "none",
29200                     'pointer-events': 'none'
29201                 }
29202               ]
29203         });
29204         this.createSVG();
29205         this.svgBox = this.svgEl.dom.getScreenCTM();
29206     },
29207     createSVG : function(){ 
29208         var svg = this.signPanel;
29209         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
29210         var t = this;
29211
29212         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
29213         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
29214         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
29215         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
29216         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
29217         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
29218         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
29219         
29220     },
29221     isTouchEvent : function(e){
29222         return e.type.match(/^touch/);
29223     },
29224     getCoords : function (e) {
29225         var pt    = this.svgEl.dom.createSVGPoint();
29226         pt.x = e.clientX; 
29227         pt.y = e.clientY;
29228         if (this.isTouchEvent(e)) {
29229             pt.x =  e.targetTouches[0].clientX;
29230             pt.y = e.targetTouches[0].clientY;
29231         }
29232         var a = this.svgEl.dom.getScreenCTM();
29233         var b = a.inverse();
29234         var mx = pt.matrixTransform(b);
29235         return mx.x + ',' + mx.y;
29236     },
29237     //mouse event headler 
29238     down : function (e) {
29239         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
29240         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
29241         
29242         this.isMouseDown = true;
29243         
29244         e.preventDefault();
29245     },
29246     move : function (e) {
29247         if (this.isMouseDown) {
29248             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
29249             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
29250         }
29251         
29252         e.preventDefault();
29253     },
29254     up : function (e) {
29255         this.isMouseDown = false;
29256         var sp = this.signatureTmp.split(' ');
29257         
29258         if(sp.length > 1){
29259             if(!sp[sp.length-2].match(/^L/)){
29260                 sp.pop();
29261                 sp.pop();
29262                 sp.push("");
29263                 this.signatureTmp = sp.join(" ");
29264             }
29265         }
29266         if(this.getValue() != this.signatureTmp){
29267             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29268             this.isConfirmed = false;
29269         }
29270         e.preventDefault();
29271     },
29272     
29273     /**
29274      * Protected method that will not generally be called directly. It
29275      * is called when the editor creates its toolbar. Override this method if you need to
29276      * add custom toolbar buttons.
29277      * @param {HtmlEditor} editor
29278      */
29279     createToolbar : function(editor){
29280          function btn(id, toggle, handler){
29281             var xid = fid + '-'+ id ;
29282             return {
29283                 id : xid,
29284                 cmd : id,
29285                 cls : 'x-btn-icon x-edit-'+id,
29286                 enableToggle:toggle !== false,
29287                 scope: editor, // was editor...
29288                 handler:handler||editor.relayBtnCmd,
29289                 clickEvent:'mousedown',
29290                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
29291                 tabIndex:-1
29292             };
29293         }
29294         
29295         
29296         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
29297         this.tb = tb;
29298         this.tb.add(
29299            {
29300                 cls : ' x-signature-btn x-signature-'+id,
29301                 scope: editor, // was editor...
29302                 handler: this.reset,
29303                 clickEvent:'mousedown',
29304                 text: this.labels.clear
29305             },
29306             {
29307                  xtype : 'Fill',
29308                  xns: Roo.Toolbar
29309             }, 
29310             {
29311                 cls : '  x-signature-btn x-signature-'+id,
29312                 scope: editor, // was editor...
29313                 handler: this.confirmHandler,
29314                 clickEvent:'mousedown',
29315                 text: this.labels.confirm
29316             }
29317         );
29318     
29319     },
29320     //public
29321     /**
29322      * when user is clicked confirm then show this image.....
29323      * 
29324      * @return {String} Image Data URI
29325      */
29326     getImageDataURI : function(){
29327         var svg = this.svgEl.dom.parentNode.innerHTML;
29328         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
29329         return src; 
29330     },
29331     /**
29332      * 
29333      * @return {Boolean} this.isConfirmed
29334      */
29335     getConfirmed : function(){
29336         return this.isConfirmed;
29337     },
29338     /**
29339      * 
29340      * @return {Number} this.width
29341      */
29342     getWidth : function(){
29343         return this.width;
29344     },
29345     /**
29346      * 
29347      * @return {Number} this.height
29348      */
29349     getHeight : function(){
29350         return this.height;
29351     },
29352     // private
29353     getSignature : function(){
29354         return this.signatureTmp;
29355     },
29356     // private
29357     reset : function(){
29358         this.signatureTmp = '';
29359         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29360         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
29361         this.isConfirmed = false;
29362         Roo.form.Signature.superclass.reset.call(this);
29363     },
29364     setSignature : function(s){
29365         this.signatureTmp = s;
29366         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29367         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
29368         this.setValue(s);
29369         this.isConfirmed = false;
29370         Roo.form.Signature.superclass.reset.call(this);
29371     }, 
29372     test : function(){
29373 //        Roo.log(this.signPanel.dom.contentWindow.up())
29374     },
29375     //private
29376     setConfirmed : function(){
29377         
29378         
29379         
29380 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
29381     },
29382     // private
29383     confirmHandler : function(){
29384         if(!this.getSignature()){
29385             return;
29386         }
29387         
29388         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
29389         this.setValue(this.getSignature());
29390         this.isConfirmed = true;
29391         
29392         this.fireEvent('confirm', this);
29393     },
29394     // private
29395     // Subclasses should provide the validation implementation by overriding this
29396     validateValue : function(value){
29397         if(this.allowBlank){
29398             return true;
29399         }
29400         
29401         if(this.isConfirmed){
29402             return true;
29403         }
29404         return false;
29405     }
29406 });/*
29407  * Based on:
29408  * Ext JS Library 1.1.1
29409  * Copyright(c) 2006-2007, Ext JS, LLC.
29410  *
29411  * Originally Released Under LGPL - original licence link has changed is not relivant.
29412  *
29413  * Fork - LGPL
29414  * <script type="text/javascript">
29415  */
29416  
29417
29418 /**
29419  * @class Roo.form.ComboBox
29420  * @extends Roo.form.TriggerField
29421  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
29422  * @constructor
29423  * Create a new ComboBox.
29424  * @param {Object} config Configuration options
29425  */
29426 Roo.form.Select = function(config){
29427     Roo.form.Select.superclass.constructor.call(this, config);
29428      
29429 };
29430
29431 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
29432     /**
29433      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
29434      */
29435     /**
29436      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
29437      * rendering into an Roo.Editor, defaults to false)
29438      */
29439     /**
29440      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
29441      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
29442      */
29443     /**
29444      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
29445      */
29446     /**
29447      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
29448      * the dropdown list (defaults to undefined, with no header element)
29449      */
29450
29451      /**
29452      * @cfg {String/Roo.Template} tpl The template to use to render the output
29453      */
29454      
29455     // private
29456     defaultAutoCreate : {tag: "select"  },
29457     /**
29458      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
29459      */
29460     listWidth: undefined,
29461     /**
29462      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
29463      * mode = 'remote' or 'text' if mode = 'local')
29464      */
29465     displayField: undefined,
29466     /**
29467      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
29468      * mode = 'remote' or 'value' if mode = 'local'). 
29469      * Note: use of a valueField requires the user make a selection
29470      * in order for a value to be mapped.
29471      */
29472     valueField: undefined,
29473     
29474     
29475     /**
29476      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
29477      * field's data value (defaults to the underlying DOM element's name)
29478      */
29479     hiddenName: undefined,
29480     /**
29481      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
29482      */
29483     listClass: '',
29484     /**
29485      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
29486      */
29487     selectedClass: 'x-combo-selected',
29488     /**
29489      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
29490      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
29491      * which displays a downward arrow icon).
29492      */
29493     triggerClass : 'x-form-arrow-trigger',
29494     /**
29495      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29496      */
29497     shadow:'sides',
29498     /**
29499      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
29500      * anchor positions (defaults to 'tl-bl')
29501      */
29502     listAlign: 'tl-bl?',
29503     /**
29504      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
29505      */
29506     maxHeight: 300,
29507     /**
29508      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
29509      * query specified by the allQuery config option (defaults to 'query')
29510      */
29511     triggerAction: 'query',
29512     /**
29513      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
29514      * (defaults to 4, does not apply if editable = false)
29515      */
29516     minChars : 4,
29517     /**
29518      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
29519      * delay (typeAheadDelay) if it matches a known value (defaults to false)
29520      */
29521     typeAhead: false,
29522     /**
29523      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
29524      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
29525      */
29526     queryDelay: 500,
29527     /**
29528      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
29529      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
29530      */
29531     pageSize: 0,
29532     /**
29533      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
29534      * when editable = true (defaults to false)
29535      */
29536     selectOnFocus:false,
29537     /**
29538      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
29539      */
29540     queryParam: 'query',
29541     /**
29542      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
29543      * when mode = 'remote' (defaults to 'Loading...')
29544      */
29545     loadingText: 'Loading...',
29546     /**
29547      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
29548      */
29549     resizable: false,
29550     /**
29551      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
29552      */
29553     handleHeight : 8,
29554     /**
29555      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
29556      * traditional select (defaults to true)
29557      */
29558     editable: true,
29559     /**
29560      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
29561      */
29562     allQuery: '',
29563     /**
29564      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
29565      */
29566     mode: 'remote',
29567     /**
29568      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
29569      * listWidth has a higher value)
29570      */
29571     minListWidth : 70,
29572     /**
29573      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
29574      * allow the user to set arbitrary text into the field (defaults to false)
29575      */
29576     forceSelection:false,
29577     /**
29578      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
29579      * if typeAhead = true (defaults to 250)
29580      */
29581     typeAheadDelay : 250,
29582     /**
29583      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
29584      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
29585      */
29586     valueNotFoundText : undefined,
29587     
29588     /**
29589      * @cfg {String} defaultValue The value displayed after loading the store.
29590      */
29591     defaultValue: '',
29592     
29593     /**
29594      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
29595      */
29596     blockFocus : false,
29597     
29598     /**
29599      * @cfg {Boolean} disableClear Disable showing of clear button.
29600      */
29601     disableClear : false,
29602     /**
29603      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
29604      */
29605     alwaysQuery : false,
29606     
29607     //private
29608     addicon : false,
29609     editicon: false,
29610     
29611     // element that contains real text value.. (when hidden is used..)
29612      
29613     // private
29614     onRender : function(ct, position){
29615         Roo.form.Field.prototype.onRender.call(this, ct, position);
29616         
29617         if(this.store){
29618             this.store.on('beforeload', this.onBeforeLoad, this);
29619             this.store.on('load', this.onLoad, this);
29620             this.store.on('loadexception', this.onLoadException, this);
29621             this.store.load({});
29622         }
29623         
29624         
29625         
29626     },
29627
29628     // private
29629     initEvents : function(){
29630         //Roo.form.ComboBox.superclass.initEvents.call(this);
29631  
29632     },
29633
29634     onDestroy : function(){
29635        
29636         if(this.store){
29637             this.store.un('beforeload', this.onBeforeLoad, this);
29638             this.store.un('load', this.onLoad, this);
29639             this.store.un('loadexception', this.onLoadException, this);
29640         }
29641         //Roo.form.ComboBox.superclass.onDestroy.call(this);
29642     },
29643
29644     // private
29645     fireKey : function(e){
29646         if(e.isNavKeyPress() && !this.list.isVisible()){
29647             this.fireEvent("specialkey", this, e);
29648         }
29649     },
29650
29651     // private
29652     onResize: function(w, h){
29653         
29654         return; 
29655     
29656         
29657     },
29658
29659     /**
29660      * Allow or prevent the user from directly editing the field text.  If false is passed,
29661      * the user will only be able to select from the items defined in the dropdown list.  This method
29662      * is the runtime equivalent of setting the 'editable' config option at config time.
29663      * @param {Boolean} value True to allow the user to directly edit the field text
29664      */
29665     setEditable : function(value){
29666          
29667     },
29668
29669     // private
29670     onBeforeLoad : function(){
29671         
29672         Roo.log("Select before load");
29673         return;
29674     
29675         this.innerList.update(this.loadingText ?
29676                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
29677         //this.restrictHeight();
29678         this.selectedIndex = -1;
29679     },
29680
29681     // private
29682     onLoad : function(){
29683
29684     
29685         var dom = this.el.dom;
29686         dom.innerHTML = '';
29687          var od = dom.ownerDocument;
29688          
29689         if (this.emptyText) {
29690             var op = od.createElement('option');
29691             op.setAttribute('value', '');
29692             op.innerHTML = String.format('{0}', this.emptyText);
29693             dom.appendChild(op);
29694         }
29695         if(this.store.getCount() > 0){
29696            
29697             var vf = this.valueField;
29698             var df = this.displayField;
29699             this.store.data.each(function(r) {
29700                 // which colmsn to use... testing - cdoe / title..
29701                 var op = od.createElement('option');
29702                 op.setAttribute('value', r.data[vf]);
29703                 op.innerHTML = String.format('{0}', r.data[df]);
29704                 dom.appendChild(op);
29705             });
29706             if (typeof(this.defaultValue != 'undefined')) {
29707                 this.setValue(this.defaultValue);
29708             }
29709             
29710              
29711         }else{
29712             //this.onEmptyResults();
29713         }
29714         //this.el.focus();
29715     },
29716     // private
29717     onLoadException : function()
29718     {
29719         dom.innerHTML = '';
29720             
29721         Roo.log("Select on load exception");
29722         return;
29723     
29724         this.collapse();
29725         Roo.log(this.store.reader.jsonData);
29726         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
29727             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
29728         }
29729         
29730         
29731     },
29732     // private
29733     onTypeAhead : function(){
29734          
29735     },
29736
29737     // private
29738     onSelect : function(record, index){
29739         Roo.log('on select?');
29740         return;
29741         if(this.fireEvent('beforeselect', this, record, index) !== false){
29742             this.setFromData(index > -1 ? record.data : false);
29743             this.collapse();
29744             this.fireEvent('select', this, record, index);
29745         }
29746     },
29747
29748     /**
29749      * Returns the currently selected field value or empty string if no value is set.
29750      * @return {String} value The selected value
29751      */
29752     getValue : function(){
29753         var dom = this.el.dom;
29754         this.value = dom.options[dom.selectedIndex].value;
29755         return this.value;
29756         
29757     },
29758
29759     /**
29760      * Clears any text/value currently set in the field
29761      */
29762     clearValue : function(){
29763         this.value = '';
29764         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
29765         
29766     },
29767
29768     /**
29769      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
29770      * will be displayed in the field.  If the value does not match the data value of an existing item,
29771      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
29772      * Otherwise the field will be blank (although the value will still be set).
29773      * @param {String} value The value to match
29774      */
29775     setValue : function(v){
29776         var d = this.el.dom;
29777         for (var i =0; i < d.options.length;i++) {
29778             if (v == d.options[i].value) {
29779                 d.selectedIndex = i;
29780                 this.value = v;
29781                 return;
29782             }
29783         }
29784         this.clearValue();
29785     },
29786     /**
29787      * @property {Object} the last set data for the element
29788      */
29789     
29790     lastData : false,
29791     /**
29792      * Sets the value of the field based on a object which is related to the record format for the store.
29793      * @param {Object} value the value to set as. or false on reset?
29794      */
29795     setFromData : function(o){
29796         Roo.log('setfrom data?');
29797          
29798         
29799         
29800     },
29801     // private
29802     reset : function(){
29803         this.clearValue();
29804     },
29805     // private
29806     findRecord : function(prop, value){
29807         
29808         return false;
29809     
29810         var record;
29811         if(this.store.getCount() > 0){
29812             this.store.each(function(r){
29813                 if(r.data[prop] == value){
29814                     record = r;
29815                     return false;
29816                 }
29817                 return true;
29818             });
29819         }
29820         return record;
29821     },
29822     
29823     getName: function()
29824     {
29825         // returns hidden if it's set..
29826         if (!this.rendered) {return ''};
29827         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
29828         
29829     },
29830      
29831
29832     
29833
29834     // private
29835     onEmptyResults : function(){
29836         Roo.log('empty results');
29837         //this.collapse();
29838     },
29839
29840     /**
29841      * Returns true if the dropdown list is expanded, else false.
29842      */
29843     isExpanded : function(){
29844         return false;
29845     },
29846
29847     /**
29848      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
29849      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
29850      * @param {String} value The data value of the item to select
29851      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
29852      * selected item if it is not currently in view (defaults to true)
29853      * @return {Boolean} True if the value matched an item in the list, else false
29854      */
29855     selectByValue : function(v, scrollIntoView){
29856         Roo.log('select By Value');
29857         return false;
29858     
29859         if(v !== undefined && v !== null){
29860             var r = this.findRecord(this.valueField || this.displayField, v);
29861             if(r){
29862                 this.select(this.store.indexOf(r), scrollIntoView);
29863                 return true;
29864             }
29865         }
29866         return false;
29867     },
29868
29869     /**
29870      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
29871      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
29872      * @param {Number} index The zero-based index of the list item to select
29873      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
29874      * selected item if it is not currently in view (defaults to true)
29875      */
29876     select : function(index, scrollIntoView){
29877         Roo.log('select ');
29878         return  ;
29879         
29880         this.selectedIndex = index;
29881         this.view.select(index);
29882         if(scrollIntoView !== false){
29883             var el = this.view.getNode(index);
29884             if(el){
29885                 this.innerList.scrollChildIntoView(el, false);
29886             }
29887         }
29888     },
29889
29890       
29891
29892     // private
29893     validateBlur : function(){
29894         
29895         return;
29896         
29897     },
29898
29899     // private
29900     initQuery : function(){
29901         this.doQuery(this.getRawValue());
29902     },
29903
29904     // private
29905     doForce : function(){
29906         if(this.el.dom.value.length > 0){
29907             this.el.dom.value =
29908                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
29909              
29910         }
29911     },
29912
29913     /**
29914      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
29915      * query allowing the query action to be canceled if needed.
29916      * @param {String} query The SQL query to execute
29917      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
29918      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
29919      * saved in the current store (defaults to false)
29920      */
29921     doQuery : function(q, forceAll){
29922         
29923         Roo.log('doQuery?');
29924         if(q === undefined || q === null){
29925             q = '';
29926         }
29927         var qe = {
29928             query: q,
29929             forceAll: forceAll,
29930             combo: this,
29931             cancel:false
29932         };
29933         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
29934             return false;
29935         }
29936         q = qe.query;
29937         forceAll = qe.forceAll;
29938         if(forceAll === true || (q.length >= this.minChars)){
29939             if(this.lastQuery != q || this.alwaysQuery){
29940                 this.lastQuery = q;
29941                 if(this.mode == 'local'){
29942                     this.selectedIndex = -1;
29943                     if(forceAll){
29944                         this.store.clearFilter();
29945                     }else{
29946                         this.store.filter(this.displayField, q);
29947                     }
29948                     this.onLoad();
29949                 }else{
29950                     this.store.baseParams[this.queryParam] = q;
29951                     this.store.load({
29952                         params: this.getParams(q)
29953                     });
29954                     this.expand();
29955                 }
29956             }else{
29957                 this.selectedIndex = -1;
29958                 this.onLoad();   
29959             }
29960         }
29961     },
29962
29963     // private
29964     getParams : function(q){
29965         var p = {};
29966         //p[this.queryParam] = q;
29967         if(this.pageSize){
29968             p.start = 0;
29969             p.limit = this.pageSize;
29970         }
29971         return p;
29972     },
29973
29974     /**
29975      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
29976      */
29977     collapse : function(){
29978         
29979     },
29980
29981     // private
29982     collapseIf : function(e){
29983         
29984     },
29985
29986     /**
29987      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
29988      */
29989     expand : function(){
29990         
29991     } ,
29992
29993     // private
29994      
29995
29996     /** 
29997     * @cfg {Boolean} grow 
29998     * @hide 
29999     */
30000     /** 
30001     * @cfg {Number} growMin 
30002     * @hide 
30003     */
30004     /** 
30005     * @cfg {Number} growMax 
30006     * @hide 
30007     */
30008     /**
30009      * @hide
30010      * @method autoSize
30011      */
30012     
30013     setWidth : function()
30014     {
30015         
30016     },
30017     getResizeEl : function(){
30018         return this.el;
30019     }
30020 });//<script type="text/javasscript">
30021  
30022
30023 /**
30024  * @class Roo.DDView
30025  * A DnD enabled version of Roo.View.
30026  * @param {Element/String} container The Element in which to create the View.
30027  * @param {String} tpl The template string used to create the markup for each element of the View
30028  * @param {Object} config The configuration properties. These include all the config options of
30029  * {@link Roo.View} plus some specific to this class.<br>
30030  * <p>
30031  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30032  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30033  * <p>
30034  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30035 .x-view-drag-insert-above {
30036         border-top:1px dotted #3366cc;
30037 }
30038 .x-view-drag-insert-below {
30039         border-bottom:1px dotted #3366cc;
30040 }
30041 </code></pre>
30042  * 
30043  */
30044  
30045 Roo.DDView = function(container, tpl, config) {
30046     Roo.DDView.superclass.constructor.apply(this, arguments);
30047     this.getEl().setStyle("outline", "0px none");
30048     this.getEl().unselectable();
30049     if (this.dragGroup) {
30050         this.setDraggable(this.dragGroup.split(","));
30051     }
30052     if (this.dropGroup) {
30053         this.setDroppable(this.dropGroup.split(","));
30054     }
30055     if (this.deletable) {
30056         this.setDeletable();
30057     }
30058     this.isDirtyFlag = false;
30059         this.addEvents({
30060                 "drop" : true
30061         });
30062 };
30063
30064 Roo.extend(Roo.DDView, Roo.View, {
30065 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30066 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30067 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30068 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30069
30070         isFormField: true,
30071
30072         reset: Roo.emptyFn,
30073         
30074         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30075
30076         validate: function() {
30077                 return true;
30078         },
30079         
30080         destroy: function() {
30081                 this.purgeListeners();
30082                 this.getEl.removeAllListeners();
30083                 this.getEl().remove();
30084                 if (this.dragZone) {
30085                         if (this.dragZone.destroy) {
30086                                 this.dragZone.destroy();
30087                         }
30088                 }
30089                 if (this.dropZone) {
30090                         if (this.dropZone.destroy) {
30091                                 this.dropZone.destroy();
30092                         }
30093                 }
30094         },
30095
30096 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30097         getName: function() {
30098                 return this.name;
30099         },
30100
30101 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30102         setValue: function(v) {
30103                 if (!this.store) {
30104                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30105                 }
30106                 var data = {};
30107                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30108                 this.store.proxy = new Roo.data.MemoryProxy(data);
30109                 this.store.load();
30110         },
30111
30112 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30113         getValue: function() {
30114                 var result = '(';
30115                 this.store.each(function(rec) {
30116                         result += rec.id + ',';
30117                 });
30118                 return result.substr(0, result.length - 1) + ')';
30119         },
30120         
30121         getIds: function() {
30122                 var i = 0, result = new Array(this.store.getCount());
30123                 this.store.each(function(rec) {
30124                         result[i++] = rec.id;
30125                 });
30126                 return result;
30127         },
30128         
30129         isDirty: function() {
30130                 return this.isDirtyFlag;
30131         },
30132
30133 /**
30134  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30135  *      whole Element becomes the target, and this causes the drop gesture to append.
30136  */
30137     getTargetFromEvent : function(e) {
30138                 var target = e.getTarget();
30139                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30140                 target = target.parentNode;
30141                 }
30142                 if (!target) {
30143                         target = this.el.dom.lastChild || this.el.dom;
30144                 }
30145                 return target;
30146     },
30147
30148 /**
30149  *      Create the drag data which consists of an object which has the property "ddel" as
30150  *      the drag proxy element. 
30151  */
30152     getDragData : function(e) {
30153         var target = this.findItemFromChild(e.getTarget());
30154                 if(target) {
30155                         this.handleSelection(e);
30156                         var selNodes = this.getSelectedNodes();
30157             var dragData = {
30158                 source: this,
30159                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30160                 nodes: selNodes,
30161                 records: []
30162                         };
30163                         var selectedIndices = this.getSelectedIndexes();
30164                         for (var i = 0; i < selectedIndices.length; i++) {
30165                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30166                         }
30167                         if (selNodes.length == 1) {
30168                                 dragData.ddel = target.cloneNode(true); // the div element
30169                         } else {
30170                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30171                                 div.className = 'multi-proxy';
30172                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30173                                         div.appendChild(selNodes[i].cloneNode(true));
30174                                 }
30175                                 dragData.ddel = div;
30176                         }
30177             //console.log(dragData)
30178             //console.log(dragData.ddel.innerHTML)
30179                         return dragData;
30180                 }
30181         //console.log('nodragData')
30182                 return false;
30183     },
30184     
30185 /**     Specify to which ddGroup items in this DDView may be dragged. */
30186     setDraggable: function(ddGroup) {
30187         if (ddGroup instanceof Array) {
30188                 Roo.each(ddGroup, this.setDraggable, this);
30189                 return;
30190         }
30191         if (this.dragZone) {
30192                 this.dragZone.addToGroup(ddGroup);
30193         } else {
30194                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30195                                 containerScroll: true,
30196                                 ddGroup: ddGroup 
30197
30198                         });
30199 //                      Draggability implies selection. DragZone's mousedown selects the element.
30200                         if (!this.multiSelect) { this.singleSelect = true; }
30201
30202 //                      Wire the DragZone's handlers up to methods in *this*
30203                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30204                 }
30205     },
30206
30207 /**     Specify from which ddGroup this DDView accepts drops. */
30208     setDroppable: function(ddGroup) {
30209         if (ddGroup instanceof Array) {
30210                 Roo.each(ddGroup, this.setDroppable, this);
30211                 return;
30212         }
30213         if (this.dropZone) {
30214                 this.dropZone.addToGroup(ddGroup);
30215         } else {
30216                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30217                                 containerScroll: true,
30218                                 ddGroup: ddGroup
30219                         });
30220
30221 //                      Wire the DropZone's handlers up to methods in *this*
30222                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30223                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30224                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30225                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30226                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30227                 }
30228     },
30229
30230 /**     Decide whether to drop above or below a View node. */
30231     getDropPoint : function(e, n, dd){
30232         if (n == this.el.dom) { return "above"; }
30233                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30234                 var c = t + (b - t) / 2;
30235                 var y = Roo.lib.Event.getPageY(e);
30236                 if(y <= c) {
30237                         return "above";
30238                 }else{
30239                         return "below";
30240                 }
30241     },
30242
30243     onNodeEnter : function(n, dd, e, data){
30244                 return false;
30245     },
30246     
30247     onNodeOver : function(n, dd, e, data){
30248                 var pt = this.getDropPoint(e, n, dd);
30249                 // set the insert point style on the target node
30250                 var dragElClass = this.dropNotAllowed;
30251                 if (pt) {
30252                         var targetElClass;
30253                         if (pt == "above"){
30254                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30255                                 targetElClass = "x-view-drag-insert-above";
30256                         } else {
30257                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30258                                 targetElClass = "x-view-drag-insert-below";
30259                         }
30260                         if (this.lastInsertClass != targetElClass){
30261                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30262                                 this.lastInsertClass = targetElClass;
30263                         }
30264                 }
30265                 return dragElClass;
30266         },
30267
30268     onNodeOut : function(n, dd, e, data){
30269                 this.removeDropIndicators(n);
30270     },
30271
30272     onNodeDrop : function(n, dd, e, data){
30273         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30274                 return false;
30275         }
30276         var pt = this.getDropPoint(e, n, dd);
30277                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30278                 if (pt == "below") { insertAt++; }
30279                 for (var i = 0; i < data.records.length; i++) {
30280                         var r = data.records[i];
30281                         var dup = this.store.getById(r.id);
30282                         if (dup && (dd != this.dragZone)) {
30283                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30284                         } else {
30285                                 if (data.copy) {
30286                                         this.store.insert(insertAt++, r.copy());
30287                                 } else {
30288                                         data.source.isDirtyFlag = true;
30289                                         r.store.remove(r);
30290                                         this.store.insert(insertAt++, r);
30291                                 }
30292                                 this.isDirtyFlag = true;
30293                         }
30294                 }
30295                 this.dragZone.cachedTarget = null;
30296                 return true;
30297     },
30298
30299     removeDropIndicators : function(n){
30300                 if(n){
30301                         Roo.fly(n).removeClass([
30302                                 "x-view-drag-insert-above",
30303                                 "x-view-drag-insert-below"]);
30304                         this.lastInsertClass = "_noclass";
30305                 }
30306     },
30307
30308 /**
30309  *      Utility method. Add a delete option to the DDView's context menu.
30310  *      @param {String} imageUrl The URL of the "delete" icon image.
30311  */
30312         setDeletable: function(imageUrl) {
30313                 if (!this.singleSelect && !this.multiSelect) {
30314                         this.singleSelect = true;
30315                 }
30316                 var c = this.getContextMenu();
30317                 this.contextMenu.on("itemclick", function(item) {
30318                         switch (item.id) {
30319                                 case "delete":
30320                                         this.remove(this.getSelectedIndexes());
30321                                         break;
30322                         }
30323                 }, this);
30324                 this.contextMenu.add({
30325                         icon: imageUrl,
30326                         id: "delete",
30327                         text: 'Delete'
30328                 });
30329         },
30330         
30331 /**     Return the context menu for this DDView. */
30332         getContextMenu: function() {
30333                 if (!this.contextMenu) {
30334 //                      Create the View's context menu
30335                         this.contextMenu = new Roo.menu.Menu({
30336                                 id: this.id + "-contextmenu"
30337                         });
30338                         this.el.on("contextmenu", this.showContextMenu, this);
30339                 }
30340                 return this.contextMenu;
30341         },
30342         
30343         disableContextMenu: function() {
30344                 if (this.contextMenu) {
30345                         this.el.un("contextmenu", this.showContextMenu, this);
30346                 }
30347         },
30348
30349         showContextMenu: function(e, item) {
30350         item = this.findItemFromChild(e.getTarget());
30351                 if (item) {
30352                         e.stopEvent();
30353                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30354                         this.contextMenu.showAt(e.getXY());
30355             }
30356     },
30357
30358 /**
30359  *      Remove {@link Roo.data.Record}s at the specified indices.
30360  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30361  */
30362     remove: function(selectedIndices) {
30363                 selectedIndices = [].concat(selectedIndices);
30364                 for (var i = 0; i < selectedIndices.length; i++) {
30365                         var rec = this.store.getAt(selectedIndices[i]);
30366                         this.store.remove(rec);
30367                 }
30368     },
30369
30370 /**
30371  *      Double click fires the event, but also, if this is draggable, and there is only one other
30372  *      related DropZone, it transfers the selected node.
30373  */
30374     onDblClick : function(e){
30375         var item = this.findItemFromChild(e.getTarget());
30376         if(item){
30377             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30378                 return false;
30379             }
30380             if (this.dragGroup) {
30381                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30382                     while (targets.indexOf(this.dropZone) > -1) {
30383                             targets.remove(this.dropZone);
30384                                 }
30385                     if (targets.length == 1) {
30386                                         this.dragZone.cachedTarget = null;
30387                         var el = Roo.get(targets[0].getEl());
30388                         var box = el.getBox(true);
30389                         targets[0].onNodeDrop(el.dom, {
30390                                 target: el.dom,
30391                                 xy: [box.x, box.y + box.height - 1]
30392                         }, null, this.getDragData(e));
30393                     }
30394                 }
30395         }
30396     },
30397     
30398     handleSelection: function(e) {
30399                 this.dragZone.cachedTarget = null;
30400         var item = this.findItemFromChild(e.getTarget());
30401         if (!item) {
30402                 this.clearSelections(true);
30403                 return;
30404         }
30405                 if (item && (this.multiSelect || this.singleSelect)){
30406                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30407                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30408                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30409                                 this.unselect(item);
30410                         } else {
30411                                 this.select(item, this.multiSelect && e.ctrlKey);
30412                                 this.lastSelection = item;
30413                         }
30414                 }
30415     },
30416
30417     onItemClick : function(item, index, e){
30418                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30419                         return false;
30420                 }
30421                 return true;
30422     },
30423
30424     unselect : function(nodeInfo, suppressEvent){
30425                 var node = this.getNode(nodeInfo);
30426                 if(node && this.isSelected(node)){
30427                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30428                                 Roo.fly(node).removeClass(this.selectedClass);
30429                                 this.selections.remove(node);
30430                                 if(!suppressEvent){
30431                                         this.fireEvent("selectionchange", this, this.selections);
30432                                 }
30433                         }
30434                 }
30435     }
30436 });
30437 /*
30438  * Based on:
30439  * Ext JS Library 1.1.1
30440  * Copyright(c) 2006-2007, Ext JS, LLC.
30441  *
30442  * Originally Released Under LGPL - original licence link has changed is not relivant.
30443  *
30444  * Fork - LGPL
30445  * <script type="text/javascript">
30446  */
30447  
30448 /**
30449  * @class Roo.LayoutManager
30450  * @extends Roo.util.Observable
30451  * Base class for layout managers.
30452  */
30453 Roo.LayoutManager = function(container, config){
30454     Roo.LayoutManager.superclass.constructor.call(this);
30455     this.el = Roo.get(container);
30456     // ie scrollbar fix
30457     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30458         document.body.scroll = "no";
30459     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30460         this.el.position('relative');
30461     }
30462     this.id = this.el.id;
30463     this.el.addClass("x-layout-container");
30464     /** false to disable window resize monitoring @type Boolean */
30465     this.monitorWindowResize = true;
30466     this.regions = {};
30467     this.addEvents({
30468         /**
30469          * @event layout
30470          * Fires when a layout is performed. 
30471          * @param {Roo.LayoutManager} this
30472          */
30473         "layout" : true,
30474         /**
30475          * @event regionresized
30476          * Fires when the user resizes a region. 
30477          * @param {Roo.LayoutRegion} region The resized region
30478          * @param {Number} newSize The new size (width for east/west, height for north/south)
30479          */
30480         "regionresized" : true,
30481         /**
30482          * @event regioncollapsed
30483          * Fires when a region is collapsed. 
30484          * @param {Roo.LayoutRegion} region The collapsed region
30485          */
30486         "regioncollapsed" : true,
30487         /**
30488          * @event regionexpanded
30489          * Fires when a region is expanded.  
30490          * @param {Roo.LayoutRegion} region The expanded region
30491          */
30492         "regionexpanded" : true
30493     });
30494     this.updating = false;
30495     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30496 };
30497
30498 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30499     /**
30500      * Returns true if this layout is currently being updated
30501      * @return {Boolean}
30502      */
30503     isUpdating : function(){
30504         return this.updating; 
30505     },
30506     
30507     /**
30508      * Suspend the LayoutManager from doing auto-layouts while
30509      * making multiple add or remove calls
30510      */
30511     beginUpdate : function(){
30512         this.updating = true;    
30513     },
30514     
30515     /**
30516      * Restore auto-layouts and optionally disable the manager from performing a layout
30517      * @param {Boolean} noLayout true to disable a layout update 
30518      */
30519     endUpdate : function(noLayout){
30520         this.updating = false;
30521         if(!noLayout){
30522             this.layout();
30523         }    
30524     },
30525     
30526     layout: function(){
30527         
30528     },
30529     
30530     onRegionResized : function(region, newSize){
30531         this.fireEvent("regionresized", region, newSize);
30532         this.layout();
30533     },
30534     
30535     onRegionCollapsed : function(region){
30536         this.fireEvent("regioncollapsed", region);
30537     },
30538     
30539     onRegionExpanded : function(region){
30540         this.fireEvent("regionexpanded", region);
30541     },
30542         
30543     /**
30544      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30545      * performs box-model adjustments.
30546      * @return {Object} The size as an object {width: (the width), height: (the height)}
30547      */
30548     getViewSize : function(){
30549         var size;
30550         if(this.el.dom != document.body){
30551             size = this.el.getSize();
30552         }else{
30553             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30554         }
30555         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30556         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30557         return size;
30558     },
30559     
30560     /**
30561      * Returns the Element this layout is bound to.
30562      * @return {Roo.Element}
30563      */
30564     getEl : function(){
30565         return this.el;
30566     },
30567     
30568     /**
30569      * Returns the specified region.
30570      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30571      * @return {Roo.LayoutRegion}
30572      */
30573     getRegion : function(target){
30574         return this.regions[target.toLowerCase()];
30575     },
30576     
30577     onWindowResize : function(){
30578         if(this.monitorWindowResize){
30579             this.layout();
30580         }
30581     }
30582 });/*
30583  * Based on:
30584  * Ext JS Library 1.1.1
30585  * Copyright(c) 2006-2007, Ext JS, LLC.
30586  *
30587  * Originally Released Under LGPL - original licence link has changed is not relivant.
30588  *
30589  * Fork - LGPL
30590  * <script type="text/javascript">
30591  */
30592 /**
30593  * @class Roo.BorderLayout
30594  * @extends Roo.LayoutManager
30595  * @children Roo.ContentPanel
30596  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30597  * please see: <br><br>
30598  * <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>
30599  * <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>
30600  * Example:
30601  <pre><code>
30602  var layout = new Roo.BorderLayout(document.body, {
30603     north: {
30604         initialSize: 25,
30605         titlebar: false
30606     },
30607     west: {
30608         split:true,
30609         initialSize: 200,
30610         minSize: 175,
30611         maxSize: 400,
30612         titlebar: true,
30613         collapsible: true
30614     },
30615     east: {
30616         split:true,
30617         initialSize: 202,
30618         minSize: 175,
30619         maxSize: 400,
30620         titlebar: true,
30621         collapsible: true
30622     },
30623     south: {
30624         split:true,
30625         initialSize: 100,
30626         minSize: 100,
30627         maxSize: 200,
30628         titlebar: true,
30629         collapsible: true
30630     },
30631     center: {
30632         titlebar: true,
30633         autoScroll:true,
30634         resizeTabs: true,
30635         minTabWidth: 50,
30636         preferredTabWidth: 150
30637     }
30638 });
30639
30640 // shorthand
30641 var CP = Roo.ContentPanel;
30642
30643 layout.beginUpdate();
30644 layout.add("north", new CP("north", "North"));
30645 layout.add("south", new CP("south", {title: "South", closable: true}));
30646 layout.add("west", new CP("west", {title: "West"}));
30647 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30648 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30649 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30650 layout.getRegion("center").showPanel("center1");
30651 layout.endUpdate();
30652 </code></pre>
30653
30654 <b>The container the layout is rendered into can be either the body element or any other element.
30655 If it is not the body element, the container needs to either be an absolute positioned element,
30656 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30657 the container size if it is not the body element.</b>
30658
30659 * @constructor
30660 * Create a new BorderLayout
30661 * @param {String/HTMLElement/Element} container The container this layout is bound to
30662 * @param {Object} config Configuration options
30663  */
30664 Roo.BorderLayout = function(container, config){
30665     config = config || {};
30666     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30667     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30668     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30669         var target = this.factory.validRegions[i];
30670         if(config[target]){
30671             this.addRegion(target, config[target]);
30672         }
30673     }
30674 };
30675
30676 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30677         
30678         /**
30679          * @cfg {Roo.LayoutRegion} east
30680          */
30681         /**
30682          * @cfg {Roo.LayoutRegion} west
30683          */
30684         /**
30685          * @cfg {Roo.LayoutRegion} north
30686          */
30687         /**
30688          * @cfg {Roo.LayoutRegion} south
30689          */
30690         /**
30691          * @cfg {Roo.LayoutRegion} center
30692          */
30693     /**
30694      * Creates and adds a new region if it doesn't already exist.
30695      * @param {String} target The target region key (north, south, east, west or center).
30696      * @param {Object} config The regions config object
30697      * @return {BorderLayoutRegion} The new region
30698      */
30699     addRegion : function(target, config){
30700         if(!this.regions[target]){
30701             var r = this.factory.create(target, this, config);
30702             this.bindRegion(target, r);
30703         }
30704         return this.regions[target];
30705     },
30706
30707     // private (kinda)
30708     bindRegion : function(name, r){
30709         this.regions[name] = r;
30710         r.on("visibilitychange", this.layout, this);
30711         r.on("paneladded", this.layout, this);
30712         r.on("panelremoved", this.layout, this);
30713         r.on("invalidated", this.layout, this);
30714         r.on("resized", this.onRegionResized, this);
30715         r.on("collapsed", this.onRegionCollapsed, this);
30716         r.on("expanded", this.onRegionExpanded, this);
30717     },
30718
30719     /**
30720      * Performs a layout update.
30721      */
30722     layout : function(){
30723         if(this.updating) {
30724             return;
30725         }
30726         var size = this.getViewSize();
30727         var w = size.width;
30728         var h = size.height;
30729         var centerW = w;
30730         var centerH = h;
30731         var centerY = 0;
30732         var centerX = 0;
30733         //var x = 0, y = 0;
30734
30735         var rs = this.regions;
30736         var north = rs["north"];
30737         var south = rs["south"]; 
30738         var west = rs["west"];
30739         var east = rs["east"];
30740         var center = rs["center"];
30741         //if(this.hideOnLayout){ // not supported anymore
30742             //c.el.setStyle("display", "none");
30743         //}
30744         if(north && north.isVisible()){
30745             var b = north.getBox();
30746             var m = north.getMargins();
30747             b.width = w - (m.left+m.right);
30748             b.x = m.left;
30749             b.y = m.top;
30750             centerY = b.height + b.y + m.bottom;
30751             centerH -= centerY;
30752             north.updateBox(this.safeBox(b));
30753         }
30754         if(south && south.isVisible()){
30755             var b = south.getBox();
30756             var m = south.getMargins();
30757             b.width = w - (m.left+m.right);
30758             b.x = m.left;
30759             var totalHeight = (b.height + m.top + m.bottom);
30760             b.y = h - totalHeight + m.top;
30761             centerH -= totalHeight;
30762             south.updateBox(this.safeBox(b));
30763         }
30764         if(west && west.isVisible()){
30765             var b = west.getBox();
30766             var m = west.getMargins();
30767             b.height = centerH - (m.top+m.bottom);
30768             b.x = m.left;
30769             b.y = centerY + m.top;
30770             var totalWidth = (b.width + m.left + m.right);
30771             centerX += totalWidth;
30772             centerW -= totalWidth;
30773             west.updateBox(this.safeBox(b));
30774         }
30775         if(east && east.isVisible()){
30776             var b = east.getBox();
30777             var m = east.getMargins();
30778             b.height = centerH - (m.top+m.bottom);
30779             var totalWidth = (b.width + m.left + m.right);
30780             b.x = w - totalWidth + m.left;
30781             b.y = centerY + m.top;
30782             centerW -= totalWidth;
30783             east.updateBox(this.safeBox(b));
30784         }
30785         if(center){
30786             var m = center.getMargins();
30787             var centerBox = {
30788                 x: centerX + m.left,
30789                 y: centerY + m.top,
30790                 width: centerW - (m.left+m.right),
30791                 height: centerH - (m.top+m.bottom)
30792             };
30793             //if(this.hideOnLayout){
30794                 //center.el.setStyle("display", "block");
30795             //}
30796             center.updateBox(this.safeBox(centerBox));
30797         }
30798         this.el.repaint();
30799         this.fireEvent("layout", this);
30800     },
30801
30802     // private
30803     safeBox : function(box){
30804         box.width = Math.max(0, box.width);
30805         box.height = Math.max(0, box.height);
30806         return box;
30807     },
30808
30809     /**
30810      * Adds a ContentPanel (or subclass) to this layout.
30811      * @param {String} target The target region key (north, south, east, west or center).
30812      * @param {Roo.ContentPanel} panel The panel to add
30813      * @return {Roo.ContentPanel} The added panel
30814      */
30815     add : function(target, panel){
30816          
30817         target = target.toLowerCase();
30818         return this.regions[target].add(panel);
30819     },
30820
30821     /**
30822      * Remove a ContentPanel (or subclass) to this layout.
30823      * @param {String} target The target region key (north, south, east, west or center).
30824      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30825      * @return {Roo.ContentPanel} The removed panel
30826      */
30827     remove : function(target, panel){
30828         target = target.toLowerCase();
30829         return this.regions[target].remove(panel);
30830     },
30831
30832     /**
30833      * Searches all regions for a panel with the specified id
30834      * @param {String} panelId
30835      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30836      */
30837     findPanel : function(panelId){
30838         var rs = this.regions;
30839         for(var target in rs){
30840             if(typeof rs[target] != "function"){
30841                 var p = rs[target].getPanel(panelId);
30842                 if(p){
30843                     return p;
30844                 }
30845             }
30846         }
30847         return null;
30848     },
30849
30850     /**
30851      * Searches all regions for a panel with the specified id and activates (shows) it.
30852      * @param {String/ContentPanel} panelId The panels id or the panel itself
30853      * @return {Roo.ContentPanel} The shown panel or null
30854      */
30855     showPanel : function(panelId) {
30856       var rs = this.regions;
30857       for(var target in rs){
30858          var r = rs[target];
30859          if(typeof r != "function"){
30860             if(r.hasPanel(panelId)){
30861                return r.showPanel(panelId);
30862             }
30863          }
30864       }
30865       return null;
30866    },
30867
30868    /**
30869      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30870      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30871      */
30872     restoreState : function(provider){
30873         if(!provider){
30874             provider = Roo.state.Manager;
30875         }
30876         var sm = new Roo.LayoutStateManager();
30877         sm.init(this, provider);
30878     },
30879
30880     /**
30881      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30882      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30883      * a valid ContentPanel config object.  Example:
30884      * <pre><code>
30885 // Create the main layout
30886 var layout = new Roo.BorderLayout('main-ct', {
30887     west: {
30888         split:true,
30889         minSize: 175,
30890         titlebar: true
30891     },
30892     center: {
30893         title:'Components'
30894     }
30895 }, 'main-ct');
30896
30897 // Create and add multiple ContentPanels at once via configs
30898 layout.batchAdd({
30899    west: {
30900        id: 'source-files',
30901        autoCreate:true,
30902        title:'Ext Source Files',
30903        autoScroll:true,
30904        fitToFrame:true
30905    },
30906    center : {
30907        el: cview,
30908        autoScroll:true,
30909        fitToFrame:true,
30910        toolbar: tb,
30911        resizeEl:'cbody'
30912    }
30913 });
30914 </code></pre>
30915      * @param {Object} regions An object containing ContentPanel configs by region name
30916      */
30917     batchAdd : function(regions){
30918         this.beginUpdate();
30919         for(var rname in regions){
30920             var lr = this.regions[rname];
30921             if(lr){
30922                 this.addTypedPanels(lr, regions[rname]);
30923             }
30924         }
30925         this.endUpdate();
30926     },
30927
30928     // private
30929     addTypedPanels : function(lr, ps){
30930         if(typeof ps == 'string'){
30931             lr.add(new Roo.ContentPanel(ps));
30932         }
30933         else if(ps instanceof Array){
30934             for(var i =0, len = ps.length; i < len; i++){
30935                 this.addTypedPanels(lr, ps[i]);
30936             }
30937         }
30938         else if(!ps.events){ // raw config?
30939             var el = ps.el;
30940             delete ps.el; // prevent conflict
30941             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30942         }
30943         else {  // panel object assumed!
30944             lr.add(ps);
30945         }
30946     },
30947     /**
30948      * Adds a xtype elements to the layout.
30949      * <pre><code>
30950
30951 layout.addxtype({
30952        xtype : 'ContentPanel',
30953        region: 'west',
30954        items: [ .... ]
30955    }
30956 );
30957
30958 layout.addxtype({
30959         xtype : 'NestedLayoutPanel',
30960         region: 'west',
30961         layout: {
30962            center: { },
30963            west: { }   
30964         },
30965         items : [ ... list of content panels or nested layout panels.. ]
30966    }
30967 );
30968 </code></pre>
30969      * @param {Object} cfg Xtype definition of item to add.
30970      */
30971     addxtype : function(cfg)
30972     {
30973         // basically accepts a pannel...
30974         // can accept a layout region..!?!?
30975         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30976         
30977         if (!cfg.xtype.match(/Panel$/)) {
30978             return false;
30979         }
30980         var ret = false;
30981         
30982         if (typeof(cfg.region) == 'undefined') {
30983             Roo.log("Failed to add Panel, region was not set");
30984             Roo.log(cfg);
30985             return false;
30986         }
30987         var region = cfg.region;
30988         delete cfg.region;
30989         
30990           
30991         var xitems = [];
30992         if (cfg.items) {
30993             xitems = cfg.items;
30994             delete cfg.items;
30995         }
30996         var nb = false;
30997         
30998         switch(cfg.xtype) 
30999         {
31000             case 'ContentPanel':  // ContentPanel (el, cfg)
31001             case 'ScrollPanel':  // ContentPanel (el, cfg)
31002             case 'ViewPanel': 
31003                 if(cfg.autoCreate) {
31004                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31005                 } else {
31006                     var el = this.el.createChild();
31007                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31008                 }
31009                 
31010                 this.add(region, ret);
31011                 break;
31012             
31013             
31014             case 'TreePanel': // our new panel!
31015                 cfg.el = this.el.createChild();
31016                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31017                 this.add(region, ret);
31018                 break;
31019             
31020             case 'NestedLayoutPanel': 
31021                 // create a new Layout (which is  a Border Layout...
31022                 var el = this.el.createChild();
31023                 var clayout = cfg.layout;
31024                 delete cfg.layout;
31025                 clayout.items   = clayout.items  || [];
31026                 // replace this exitems with the clayout ones..
31027                 xitems = clayout.items;
31028                  
31029                 
31030                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31031                     cfg.background = false;
31032                 }
31033                 var layout = new Roo.BorderLayout(el, clayout);
31034                 
31035                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31036                 //console.log('adding nested layout panel '  + cfg.toSource());
31037                 this.add(region, ret);
31038                 nb = {}; /// find first...
31039                 break;
31040                 
31041             case 'GridPanel': 
31042             
31043                 // needs grid and region
31044                 
31045                 //var el = this.getRegion(region).el.createChild();
31046                 var el = this.el.createChild();
31047                 // create the grid first...
31048                 
31049                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31050                 delete cfg.grid;
31051                 if (region == 'center' && this.active ) {
31052                     cfg.background = false;
31053                 }
31054                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31055                 
31056                 this.add(region, ret);
31057                 if (cfg.background) {
31058                     ret.on('activate', function(gp) {
31059                         if (!gp.grid.rendered) {
31060                             gp.grid.render();
31061                         }
31062                     });
31063                 } else {
31064                     grid.render();
31065                 }
31066                 break;
31067            
31068            
31069            
31070                 
31071                 
31072                 
31073             default:
31074                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31075                     
31076                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31077                     this.add(region, ret);
31078                 } else {
31079                 
31080                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31081                     return null;
31082                 }
31083                 
31084              // GridPanel (grid, cfg)
31085             
31086         }
31087         this.beginUpdate();
31088         // add children..
31089         var region = '';
31090         var abn = {};
31091         Roo.each(xitems, function(i)  {
31092             region = nb && i.region ? i.region : false;
31093             
31094             var add = ret.addxtype(i);
31095            
31096             if (region) {
31097                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31098                 if (!i.background) {
31099                     abn[region] = nb[region] ;
31100                 }
31101             }
31102             
31103         });
31104         this.endUpdate();
31105
31106         // make the last non-background panel active..
31107         //if (nb) { Roo.log(abn); }
31108         if (nb) {
31109             
31110             for(var r in abn) {
31111                 region = this.getRegion(r);
31112                 if (region) {
31113                     // tried using nb[r], but it does not work..
31114                      
31115                     region.showPanel(abn[r]);
31116                    
31117                 }
31118             }
31119         }
31120         return ret;
31121         
31122     }
31123 });
31124
31125 /**
31126  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31127  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31128  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31129  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31130  * <pre><code>
31131 // shorthand
31132 var CP = Roo.ContentPanel;
31133
31134 var layout = Roo.BorderLayout.create({
31135     north: {
31136         initialSize: 25,
31137         titlebar: false,
31138         panels: [new CP("north", "North")]
31139     },
31140     west: {
31141         split:true,
31142         initialSize: 200,
31143         minSize: 175,
31144         maxSize: 400,
31145         titlebar: true,
31146         collapsible: true,
31147         panels: [new CP("west", {title: "West"})]
31148     },
31149     east: {
31150         split:true,
31151         initialSize: 202,
31152         minSize: 175,
31153         maxSize: 400,
31154         titlebar: true,
31155         collapsible: true,
31156         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31157     },
31158     south: {
31159         split:true,
31160         initialSize: 100,
31161         minSize: 100,
31162         maxSize: 200,
31163         titlebar: true,
31164         collapsible: true,
31165         panels: [new CP("south", {title: "South", closable: true})]
31166     },
31167     center: {
31168         titlebar: true,
31169         autoScroll:true,
31170         resizeTabs: true,
31171         minTabWidth: 50,
31172         preferredTabWidth: 150,
31173         panels: [
31174             new CP("center1", {title: "Close Me", closable: true}),
31175             new CP("center2", {title: "Center Panel", closable: false})
31176         ]
31177     }
31178 }, document.body);
31179
31180 layout.getRegion("center").showPanel("center1");
31181 </code></pre>
31182  * @param config
31183  * @param targetEl
31184  */
31185 Roo.BorderLayout.create = function(config, targetEl){
31186     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31187     layout.beginUpdate();
31188     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31189     for(var j = 0, jlen = regions.length; j < jlen; j++){
31190         var lr = regions[j];
31191         if(layout.regions[lr] && config[lr].panels){
31192             var r = layout.regions[lr];
31193             var ps = config[lr].panels;
31194             layout.addTypedPanels(r, ps);
31195         }
31196     }
31197     layout.endUpdate();
31198     return layout;
31199 };
31200
31201 // private
31202 Roo.BorderLayout.RegionFactory = {
31203     // private
31204     validRegions : ["north","south","east","west","center"],
31205
31206     // private
31207     create : function(target, mgr, config){
31208         target = target.toLowerCase();
31209         if(config.lightweight || config.basic){
31210             return new Roo.BasicLayoutRegion(mgr, config, target);
31211         }
31212         switch(target){
31213             case "north":
31214                 return new Roo.NorthLayoutRegion(mgr, config);
31215             case "south":
31216                 return new Roo.SouthLayoutRegion(mgr, config);
31217             case "east":
31218                 return new Roo.EastLayoutRegion(mgr, config);
31219             case "west":
31220                 return new Roo.WestLayoutRegion(mgr, config);
31221             case "center":
31222                 return new Roo.CenterLayoutRegion(mgr, config);
31223         }
31224         throw 'Layout region "'+target+'" not supported.';
31225     }
31226 };/*
31227  * Based on:
31228  * Ext JS Library 1.1.1
31229  * Copyright(c) 2006-2007, Ext JS, LLC.
31230  *
31231  * Originally Released Under LGPL - original licence link has changed is not relivant.
31232  *
31233  * Fork - LGPL
31234  * <script type="text/javascript">
31235  */
31236  
31237 /**
31238  * @class Roo.BasicLayoutRegion
31239  * @extends Roo.util.Observable
31240  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31241  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31242  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31243  */
31244 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31245     this.mgr = mgr;
31246     this.position  = pos;
31247     this.events = {
31248         /**
31249          * @scope Roo.BasicLayoutRegion
31250          */
31251         
31252         /**
31253          * @event beforeremove
31254          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31255          * @param {Roo.LayoutRegion} this
31256          * @param {Roo.ContentPanel} panel The panel
31257          * @param {Object} e The cancel event object
31258          */
31259         "beforeremove" : true,
31260         /**
31261          * @event invalidated
31262          * Fires when the layout for this region is changed.
31263          * @param {Roo.LayoutRegion} this
31264          */
31265         "invalidated" : true,
31266         /**
31267          * @event visibilitychange
31268          * Fires when this region is shown or hidden 
31269          * @param {Roo.LayoutRegion} this
31270          * @param {Boolean} visibility true or false
31271          */
31272         "visibilitychange" : true,
31273         /**
31274          * @event paneladded
31275          * Fires when a panel is added. 
31276          * @param {Roo.LayoutRegion} this
31277          * @param {Roo.ContentPanel} panel The panel
31278          */
31279         "paneladded" : true,
31280         /**
31281          * @event panelremoved
31282          * Fires when a panel is removed. 
31283          * @param {Roo.LayoutRegion} this
31284          * @param {Roo.ContentPanel} panel The panel
31285          */
31286         "panelremoved" : true,
31287         /**
31288          * @event beforecollapse
31289          * Fires when this region before collapse.
31290          * @param {Roo.LayoutRegion} this
31291          */
31292         "beforecollapse" : true,
31293         /**
31294          * @event collapsed
31295          * Fires when this region is collapsed.
31296          * @param {Roo.LayoutRegion} this
31297          */
31298         "collapsed" : true,
31299         /**
31300          * @event expanded
31301          * Fires when this region is expanded.
31302          * @param {Roo.LayoutRegion} this
31303          */
31304         "expanded" : true,
31305         /**
31306          * @event slideshow
31307          * Fires when this region is slid into view.
31308          * @param {Roo.LayoutRegion} this
31309          */
31310         "slideshow" : true,
31311         /**
31312          * @event slidehide
31313          * Fires when this region slides out of view. 
31314          * @param {Roo.LayoutRegion} this
31315          */
31316         "slidehide" : true,
31317         /**
31318          * @event panelactivated
31319          * Fires when a panel is activated. 
31320          * @param {Roo.LayoutRegion} this
31321          * @param {Roo.ContentPanel} panel The activated panel
31322          */
31323         "panelactivated" : true,
31324         /**
31325          * @event resized
31326          * Fires when the user resizes this region. 
31327          * @param {Roo.LayoutRegion} this
31328          * @param {Number} newSize The new size (width for east/west, height for north/south)
31329          */
31330         "resized" : true
31331     };
31332     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31333     this.panels = new Roo.util.MixedCollection();
31334     this.panels.getKey = this.getPanelId.createDelegate(this);
31335     this.box = null;
31336     this.activePanel = null;
31337     // ensure listeners are added...
31338     
31339     if (config.listeners || config.events) {
31340         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31341             listeners : config.listeners || {},
31342             events : config.events || {}
31343         });
31344     }
31345     
31346     if(skipConfig !== true){
31347         this.applyConfig(config);
31348     }
31349 };
31350
31351 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31352     getPanelId : function(p){
31353         return p.getId();
31354     },
31355     
31356     applyConfig : function(config){
31357         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31358         this.config = config;
31359         
31360     },
31361     
31362     /**
31363      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31364      * the width, for horizontal (north, south) the height.
31365      * @param {Number} newSize The new width or height
31366      */
31367     resizeTo : function(newSize){
31368         var el = this.el ? this.el :
31369                  (this.activePanel ? this.activePanel.getEl() : null);
31370         if(el){
31371             switch(this.position){
31372                 case "east":
31373                 case "west":
31374                     el.setWidth(newSize);
31375                     this.fireEvent("resized", this, newSize);
31376                 break;
31377                 case "north":
31378                 case "south":
31379                     el.setHeight(newSize);
31380                     this.fireEvent("resized", this, newSize);
31381                 break;                
31382             }
31383         }
31384     },
31385     
31386     getBox : function(){
31387         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31388     },
31389     
31390     getMargins : function(){
31391         return this.margins;
31392     },
31393     
31394     updateBox : function(box){
31395         this.box = box;
31396         var el = this.activePanel.getEl();
31397         el.dom.style.left = box.x + "px";
31398         el.dom.style.top = box.y + "px";
31399         this.activePanel.setSize(box.width, box.height);
31400     },
31401     
31402     /**
31403      * Returns the container element for this region.
31404      * @return {Roo.Element}
31405      */
31406     getEl : function(){
31407         return this.activePanel;
31408     },
31409     
31410     /**
31411      * Returns true if this region is currently visible.
31412      * @return {Boolean}
31413      */
31414     isVisible : function(){
31415         return this.activePanel ? true : false;
31416     },
31417     
31418     setActivePanel : function(panel){
31419         panel = this.getPanel(panel);
31420         if(this.activePanel && this.activePanel != panel){
31421             this.activePanel.setActiveState(false);
31422             this.activePanel.getEl().setLeftTop(-10000,-10000);
31423         }
31424         this.activePanel = panel;
31425         panel.setActiveState(true);
31426         if(this.box){
31427             panel.setSize(this.box.width, this.box.height);
31428         }
31429         this.fireEvent("panelactivated", this, panel);
31430         this.fireEvent("invalidated");
31431     },
31432     
31433     /**
31434      * Show the specified panel.
31435      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31436      * @return {Roo.ContentPanel} The shown panel or null
31437      */
31438     showPanel : function(panel){
31439         if(panel = this.getPanel(panel)){
31440             this.setActivePanel(panel);
31441         }
31442         return panel;
31443     },
31444     
31445     /**
31446      * Get the active panel for this region.
31447      * @return {Roo.ContentPanel} The active panel or null
31448      */
31449     getActivePanel : function(){
31450         return this.activePanel;
31451     },
31452     
31453     /**
31454      * Add the passed ContentPanel(s)
31455      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31456      * @return {Roo.ContentPanel} The panel added (if only one was added)
31457      */
31458     add : function(panel){
31459         if(arguments.length > 1){
31460             for(var i = 0, len = arguments.length; i < len; i++) {
31461                 this.add(arguments[i]);
31462             }
31463             return null;
31464         }
31465         if(this.hasPanel(panel)){
31466             this.showPanel(panel);
31467             return panel;
31468         }
31469         var el = panel.getEl();
31470         if(el.dom.parentNode != this.mgr.el.dom){
31471             this.mgr.el.dom.appendChild(el.dom);
31472         }
31473         if(panel.setRegion){
31474             panel.setRegion(this);
31475         }
31476         this.panels.add(panel);
31477         el.setStyle("position", "absolute");
31478         if(!panel.background){
31479             this.setActivePanel(panel);
31480             if(this.config.initialSize && this.panels.getCount()==1){
31481                 this.resizeTo(this.config.initialSize);
31482             }
31483         }
31484         this.fireEvent("paneladded", this, panel);
31485         return panel;
31486     },
31487     
31488     /**
31489      * Returns true if the panel is in this region.
31490      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31491      * @return {Boolean}
31492      */
31493     hasPanel : function(panel){
31494         if(typeof panel == "object"){ // must be panel obj
31495             panel = panel.getId();
31496         }
31497         return this.getPanel(panel) ? true : false;
31498     },
31499     
31500     /**
31501      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31502      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31503      * @param {Boolean} preservePanel Overrides the config preservePanel option
31504      * @return {Roo.ContentPanel} The panel that was removed
31505      */
31506     remove : function(panel, preservePanel){
31507         panel = this.getPanel(panel);
31508         if(!panel){
31509             return null;
31510         }
31511         var e = {};
31512         this.fireEvent("beforeremove", this, panel, e);
31513         if(e.cancel === true){
31514             return null;
31515         }
31516         var panelId = panel.getId();
31517         this.panels.removeKey(panelId);
31518         return panel;
31519     },
31520     
31521     /**
31522      * Returns the panel specified or null if it's not in this region.
31523      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31524      * @return {Roo.ContentPanel}
31525      */
31526     getPanel : function(id){
31527         if(typeof id == "object"){ // must be panel obj
31528             return id;
31529         }
31530         return this.panels.get(id);
31531     },
31532     
31533     /**
31534      * Returns this regions position (north/south/east/west/center).
31535      * @return {String} 
31536      */
31537     getPosition: function(){
31538         return this.position;    
31539     }
31540 });/*
31541  * Based on:
31542  * Ext JS Library 1.1.1
31543  * Copyright(c) 2006-2007, Ext JS, LLC.
31544  *
31545  * Originally Released Under LGPL - original licence link has changed is not relivant.
31546  *
31547  * Fork - LGPL
31548  * <script type="text/javascript">
31549  */
31550  
31551 /**
31552  * @class Roo.LayoutRegion
31553  * @extends Roo.BasicLayoutRegion
31554  * This class represents a region in a layout manager.
31555  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31556  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31557  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31558  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31559  * @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})
31560  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
31561  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31562  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31563  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31564  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31565  * @cfg {String}    title           The title for the region (overrides panel titles)
31566  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31567  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31568  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31569  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31570  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31571  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31572  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31573  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31574  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31575  * @cfg {Boolean}   showPin         True to show a pin button
31576  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31577  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31578  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31579  * @cfg {Number}    width           For East/West panels
31580  * @cfg {Number}    height          For North/South panels
31581  * @cfg {Boolean}   split           To show the splitter
31582  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31583  */
31584 Roo.LayoutRegion = function(mgr, config, pos){
31585     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31586     var dh = Roo.DomHelper;
31587     /** This region's container element 
31588     * @type Roo.Element */
31589     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31590     /** This region's title element 
31591     * @type Roo.Element */
31592
31593     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31594         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31595         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31596     ]}, true);
31597     this.titleEl.enableDisplayMode();
31598     /** This region's title text element 
31599     * @type HTMLElement */
31600     this.titleTextEl = this.titleEl.dom.firstChild;
31601     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31602     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31603     this.closeBtn.enableDisplayMode();
31604     this.closeBtn.on("click", this.closeClicked, this);
31605     this.closeBtn.hide();
31606
31607     this.createBody(config);
31608     this.visible = true;
31609     this.collapsed = false;
31610
31611     if(config.hideWhenEmpty){
31612         this.hide();
31613         this.on("paneladded", this.validateVisibility, this);
31614         this.on("panelremoved", this.validateVisibility, this);
31615     }
31616     this.applyConfig(config);
31617 };
31618
31619 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31620
31621     createBody : function(){
31622         /** This region's body element 
31623         * @type Roo.Element */
31624         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31625     },
31626
31627     applyConfig : function(c){
31628         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31629             var dh = Roo.DomHelper;
31630             if(c.titlebar !== false){
31631                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31632                 this.collapseBtn.on("click", this.collapse, this);
31633                 this.collapseBtn.enableDisplayMode();
31634
31635                 if(c.showPin === true || this.showPin){
31636                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31637                     this.stickBtn.enableDisplayMode();
31638                     this.stickBtn.on("click", this.expand, this);
31639                     this.stickBtn.hide();
31640                 }
31641             }
31642             /** This region's collapsed element
31643             * @type Roo.Element */
31644             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31645                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31646             ]}, true);
31647             if(c.floatable !== false){
31648                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31649                this.collapsedEl.on("click", this.collapseClick, this);
31650             }
31651
31652             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31653                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31654                    id: "message", unselectable: "on", style:{"float":"left"}});
31655                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31656              }
31657             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31658             this.expandBtn.on("click", this.expand, this);
31659         }
31660         if(this.collapseBtn){
31661             this.collapseBtn.setVisible(c.collapsible == true);
31662         }
31663         this.cmargins = c.cmargins || this.cmargins ||
31664                          (this.position == "west" || this.position == "east" ?
31665                              {top: 0, left: 2, right:2, bottom: 0} :
31666                              {top: 2, left: 0, right:0, bottom: 2});
31667         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31668         this.bottomTabs = c.tabPosition != "top";
31669         this.autoScroll = c.autoScroll || false;
31670         if(this.autoScroll){
31671             this.bodyEl.setStyle("overflow", "auto");
31672         }else{
31673             this.bodyEl.setStyle("overflow", "hidden");
31674         }
31675         //if(c.titlebar !== false){
31676             if((!c.titlebar && !c.title) || c.titlebar === false){
31677                 this.titleEl.hide();
31678             }else{
31679                 this.titleEl.show();
31680                 if(c.title){
31681                     this.titleTextEl.innerHTML = c.title;
31682                 }
31683             }
31684         //}
31685         this.duration = c.duration || .30;
31686         this.slideDuration = c.slideDuration || .45;
31687         this.config = c;
31688         if(c.collapsed){
31689             this.collapse(true);
31690         }
31691         if(c.hidden){
31692             this.hide();
31693         }
31694     },
31695     /**
31696      * Returns true if this region is currently visible.
31697      * @return {Boolean}
31698      */
31699     isVisible : function(){
31700         return this.visible;
31701     },
31702
31703     /**
31704      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31705      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31706      */
31707     setCollapsedTitle : function(title){
31708         title = title || "&#160;";
31709         if(this.collapsedTitleTextEl){
31710             this.collapsedTitleTextEl.innerHTML = title;
31711         }
31712     },
31713
31714     getBox : function(){
31715         var b;
31716         if(!this.collapsed){
31717             b = this.el.getBox(false, true);
31718         }else{
31719             b = this.collapsedEl.getBox(false, true);
31720         }
31721         return b;
31722     },
31723
31724     getMargins : function(){
31725         return this.collapsed ? this.cmargins : this.margins;
31726     },
31727
31728     highlight : function(){
31729         this.el.addClass("x-layout-panel-dragover");
31730     },
31731
31732     unhighlight : function(){
31733         this.el.removeClass("x-layout-panel-dragover");
31734     },
31735
31736     updateBox : function(box){
31737         this.box = box;
31738         if(!this.collapsed){
31739             this.el.dom.style.left = box.x + "px";
31740             this.el.dom.style.top = box.y + "px";
31741             this.updateBody(box.width, box.height);
31742         }else{
31743             this.collapsedEl.dom.style.left = box.x + "px";
31744             this.collapsedEl.dom.style.top = box.y + "px";
31745             this.collapsedEl.setSize(box.width, box.height);
31746         }
31747         if(this.tabs){
31748             this.tabs.autoSizeTabs();
31749         }
31750     },
31751
31752     updateBody : function(w, h){
31753         if(w !== null){
31754             this.el.setWidth(w);
31755             w -= this.el.getBorderWidth("rl");
31756             if(this.config.adjustments){
31757                 w += this.config.adjustments[0];
31758             }
31759         }
31760         if(h !== null){
31761             this.el.setHeight(h);
31762             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31763             h -= this.el.getBorderWidth("tb");
31764             if(this.config.adjustments){
31765                 h += this.config.adjustments[1];
31766             }
31767             this.bodyEl.setHeight(h);
31768             if(this.tabs){
31769                 h = this.tabs.syncHeight(h);
31770             }
31771         }
31772         if(this.panelSize){
31773             w = w !== null ? w : this.panelSize.width;
31774             h = h !== null ? h : this.panelSize.height;
31775         }
31776         if(this.activePanel){
31777             var el = this.activePanel.getEl();
31778             w = w !== null ? w : el.getWidth();
31779             h = h !== null ? h : el.getHeight();
31780             this.panelSize = {width: w, height: h};
31781             this.activePanel.setSize(w, h);
31782         }
31783         if(Roo.isIE && this.tabs){
31784             this.tabs.el.repaint();
31785         }
31786     },
31787
31788     /**
31789      * Returns the container element for this region.
31790      * @return {Roo.Element}
31791      */
31792     getEl : function(){
31793         return this.el;
31794     },
31795
31796     /**
31797      * Hides this region.
31798      */
31799     hide : function(){
31800         if(!this.collapsed){
31801             this.el.dom.style.left = "-2000px";
31802             this.el.hide();
31803         }else{
31804             this.collapsedEl.dom.style.left = "-2000px";
31805             this.collapsedEl.hide();
31806         }
31807         this.visible = false;
31808         this.fireEvent("visibilitychange", this, false);
31809     },
31810
31811     /**
31812      * Shows this region if it was previously hidden.
31813      */
31814     show : function(){
31815         if(!this.collapsed){
31816             this.el.show();
31817         }else{
31818             this.collapsedEl.show();
31819         }
31820         this.visible = true;
31821         this.fireEvent("visibilitychange", this, true);
31822     },
31823
31824     closeClicked : function(){
31825         if(this.activePanel){
31826             this.remove(this.activePanel);
31827         }
31828     },
31829
31830     collapseClick : function(e){
31831         if(this.isSlid){
31832            e.stopPropagation();
31833            this.slideIn();
31834         }else{
31835            e.stopPropagation();
31836            this.slideOut();
31837         }
31838     },
31839
31840     /**
31841      * Collapses this region.
31842      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31843      */
31844     collapse : function(skipAnim, skipCheck){
31845         if(this.collapsed) {
31846             return;
31847         }
31848         
31849         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
31850             
31851             this.collapsed = true;
31852             if(this.split){
31853                 this.split.el.hide();
31854             }
31855             if(this.config.animate && skipAnim !== true){
31856                 this.fireEvent("invalidated", this);
31857                 this.animateCollapse();
31858             }else{
31859                 this.el.setLocation(-20000,-20000);
31860                 this.el.hide();
31861                 this.collapsedEl.show();
31862                 this.fireEvent("collapsed", this);
31863                 this.fireEvent("invalidated", this);
31864             }
31865         }
31866         
31867     },
31868
31869     animateCollapse : function(){
31870         // overridden
31871     },
31872
31873     /**
31874      * Expands this region if it was previously collapsed.
31875      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31876      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31877      */
31878     expand : function(e, skipAnim){
31879         if(e) {
31880             e.stopPropagation();
31881         }
31882         if(!this.collapsed || this.el.hasActiveFx()) {
31883             return;
31884         }
31885         if(this.isSlid){
31886             this.afterSlideIn();
31887             skipAnim = true;
31888         }
31889         this.collapsed = false;
31890         if(this.config.animate && skipAnim !== true){
31891             this.animateExpand();
31892         }else{
31893             this.el.show();
31894             if(this.split){
31895                 this.split.el.show();
31896             }
31897             this.collapsedEl.setLocation(-2000,-2000);
31898             this.collapsedEl.hide();
31899             this.fireEvent("invalidated", this);
31900             this.fireEvent("expanded", this);
31901         }
31902     },
31903
31904     animateExpand : function(){
31905         // overridden
31906     },
31907
31908     initTabs : function()
31909     {
31910         this.bodyEl.setStyle("overflow", "hidden");
31911         var ts = new Roo.TabPanel(
31912                 this.bodyEl.dom,
31913                 {
31914                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31915                     disableTooltips: this.config.disableTabTips,
31916                     toolbar : this.config.toolbar
31917                 }
31918         );
31919         if(this.config.hideTabs){
31920             ts.stripWrap.setDisplayed(false);
31921         }
31922         this.tabs = ts;
31923         ts.resizeTabs = this.config.resizeTabs === true;
31924         ts.minTabWidth = this.config.minTabWidth || 40;
31925         ts.maxTabWidth = this.config.maxTabWidth || 250;
31926         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31927         ts.monitorResize = false;
31928         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31929         ts.bodyEl.addClass('x-layout-tabs-body');
31930         this.panels.each(this.initPanelAsTab, this);
31931     },
31932
31933     initPanelAsTab : function(panel){
31934         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31935                     this.config.closeOnTab && panel.isClosable());
31936         if(panel.tabTip !== undefined){
31937             ti.setTooltip(panel.tabTip);
31938         }
31939         ti.on("activate", function(){
31940               this.setActivePanel(panel);
31941         }, this);
31942         if(this.config.closeOnTab){
31943             ti.on("beforeclose", function(t, e){
31944                 e.cancel = true;
31945                 this.remove(panel);
31946             }, this);
31947         }
31948         return ti;
31949     },
31950
31951     updatePanelTitle : function(panel, title){
31952         if(this.activePanel == panel){
31953             this.updateTitle(title);
31954         }
31955         if(this.tabs){
31956             var ti = this.tabs.getTab(panel.getEl().id);
31957             ti.setText(title);
31958             if(panel.tabTip !== undefined){
31959                 ti.setTooltip(panel.tabTip);
31960             }
31961         }
31962     },
31963
31964     updateTitle : function(title){
31965         if(this.titleTextEl && !this.config.title){
31966             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31967         }
31968     },
31969
31970     setActivePanel : function(panel){
31971         panel = this.getPanel(panel);
31972         if(this.activePanel && this.activePanel != panel){
31973             this.activePanel.setActiveState(false);
31974         }
31975         this.activePanel = panel;
31976         panel.setActiveState(true);
31977         if(this.panelSize){
31978             panel.setSize(this.panelSize.width, this.panelSize.height);
31979         }
31980         if(this.closeBtn){
31981             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31982         }
31983         this.updateTitle(panel.getTitle());
31984         if(this.tabs){
31985             this.fireEvent("invalidated", this);
31986         }
31987         this.fireEvent("panelactivated", this, panel);
31988     },
31989
31990     /**
31991      * Shows the specified panel.
31992      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31993      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31994      */
31995     showPanel : function(panel)
31996     {
31997         panel = this.getPanel(panel);
31998         if(panel){
31999             if(this.tabs){
32000                 var tab = this.tabs.getTab(panel.getEl().id);
32001                 if(tab.isHidden()){
32002                     this.tabs.unhideTab(tab.id);
32003                 }
32004                 tab.activate();
32005             }else{
32006                 this.setActivePanel(panel);
32007             }
32008         }
32009         return panel;
32010     },
32011
32012     /**
32013      * Get the active panel for this region.
32014      * @return {Roo.ContentPanel} The active panel or null
32015      */
32016     getActivePanel : function(){
32017         return this.activePanel;
32018     },
32019
32020     validateVisibility : function(){
32021         if(this.panels.getCount() < 1){
32022             this.updateTitle("&#160;");
32023             this.closeBtn.hide();
32024             this.hide();
32025         }else{
32026             if(!this.isVisible()){
32027                 this.show();
32028             }
32029         }
32030     },
32031
32032     /**
32033      * Adds the passed ContentPanel(s) to this region.
32034      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32035      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32036      */
32037     add : function(panel){
32038         if(arguments.length > 1){
32039             for(var i = 0, len = arguments.length; i < len; i++) {
32040                 this.add(arguments[i]);
32041             }
32042             return null;
32043         }
32044         if(this.hasPanel(panel)){
32045             this.showPanel(panel);
32046             return panel;
32047         }
32048         panel.setRegion(this);
32049         this.panels.add(panel);
32050         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32051             this.bodyEl.dom.appendChild(panel.getEl().dom);
32052             if(panel.background !== true){
32053                 this.setActivePanel(panel);
32054             }
32055             this.fireEvent("paneladded", this, panel);
32056             return panel;
32057         }
32058         if(!this.tabs){
32059             this.initTabs();
32060         }else{
32061             this.initPanelAsTab(panel);
32062         }
32063         if(panel.background !== true){
32064             this.tabs.activate(panel.getEl().id);
32065         }
32066         this.fireEvent("paneladded", this, panel);
32067         return panel;
32068     },
32069
32070     /**
32071      * Hides the tab for the specified panel.
32072      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32073      */
32074     hidePanel : function(panel){
32075         if(this.tabs && (panel = this.getPanel(panel))){
32076             this.tabs.hideTab(panel.getEl().id);
32077         }
32078     },
32079
32080     /**
32081      * Unhides the tab for a previously hidden panel.
32082      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32083      */
32084     unhidePanel : function(panel){
32085         if(this.tabs && (panel = this.getPanel(panel))){
32086             this.tabs.unhideTab(panel.getEl().id);
32087         }
32088     },
32089
32090     clearPanels : function(){
32091         while(this.panels.getCount() > 0){
32092              this.remove(this.panels.first());
32093         }
32094     },
32095
32096     /**
32097      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32098      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32099      * @param {Boolean} preservePanel Overrides the config preservePanel option
32100      * @return {Roo.ContentPanel} The panel that was removed
32101      */
32102     remove : function(panel, preservePanel){
32103         panel = this.getPanel(panel);
32104         if(!panel){
32105             return null;
32106         }
32107         var e = {};
32108         this.fireEvent("beforeremove", this, panel, e);
32109         if(e.cancel === true){
32110             return null;
32111         }
32112         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32113         var panelId = panel.getId();
32114         this.panels.removeKey(panelId);
32115         if(preservePanel){
32116             document.body.appendChild(panel.getEl().dom);
32117         }
32118         if(this.tabs){
32119             this.tabs.removeTab(panel.getEl().id);
32120         }else if (!preservePanel){
32121             this.bodyEl.dom.removeChild(panel.getEl().dom);
32122         }
32123         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32124             var p = this.panels.first();
32125             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32126             tempEl.appendChild(p.getEl().dom);
32127             this.bodyEl.update("");
32128             this.bodyEl.dom.appendChild(p.getEl().dom);
32129             tempEl = null;
32130             this.updateTitle(p.getTitle());
32131             this.tabs = null;
32132             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32133             this.setActivePanel(p);
32134         }
32135         panel.setRegion(null);
32136         if(this.activePanel == panel){
32137             this.activePanel = null;
32138         }
32139         if(this.config.autoDestroy !== false && preservePanel !== true){
32140             try{panel.destroy();}catch(e){}
32141         }
32142         this.fireEvent("panelremoved", this, panel);
32143         return panel;
32144     },
32145
32146     /**
32147      * Returns the TabPanel component used by this region
32148      * @return {Roo.TabPanel}
32149      */
32150     getTabs : function(){
32151         return this.tabs;
32152     },
32153
32154     createTool : function(parentEl, className){
32155         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32156             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32157         btn.addClassOnOver("x-layout-tools-button-over");
32158         return btn;
32159     }
32160 });/*
32161  * Based on:
32162  * Ext JS Library 1.1.1
32163  * Copyright(c) 2006-2007, Ext JS, LLC.
32164  *
32165  * Originally Released Under LGPL - original licence link has changed is not relivant.
32166  *
32167  * Fork - LGPL
32168  * <script type="text/javascript">
32169  */
32170  
32171
32172
32173 /**
32174  * @class Roo.SplitLayoutRegion
32175  * @extends Roo.LayoutRegion
32176  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32177  */
32178 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32179     this.cursor = cursor;
32180     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32181 };
32182
32183 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32184     splitTip : "Drag to resize.",
32185     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32186     useSplitTips : false,
32187
32188     applyConfig : function(config){
32189         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32190         if(config.split){
32191             if(!this.split){
32192                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32193                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32194                 /** The SplitBar for this region 
32195                 * @type Roo.SplitBar */
32196                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32197                 this.split.on("moved", this.onSplitMove, this);
32198                 this.split.useShim = config.useShim === true;
32199                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32200                 if(this.useSplitTips){
32201                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32202                 }
32203                 if(config.collapsible){
32204                     this.split.el.on("dblclick", this.collapse,  this);
32205                 }
32206             }
32207             if(typeof config.minSize != "undefined"){
32208                 this.split.minSize = config.minSize;
32209             }
32210             if(typeof config.maxSize != "undefined"){
32211                 this.split.maxSize = config.maxSize;
32212             }
32213             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32214                 this.hideSplitter();
32215             }
32216         }
32217     },
32218
32219     getHMaxSize : function(){
32220          var cmax = this.config.maxSize || 10000;
32221          var center = this.mgr.getRegion("center");
32222          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32223     },
32224
32225     getVMaxSize : function(){
32226          var cmax = this.config.maxSize || 10000;
32227          var center = this.mgr.getRegion("center");
32228          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32229     },
32230
32231     onSplitMove : function(split, newSize){
32232         this.fireEvent("resized", this, newSize);
32233     },
32234     
32235     /** 
32236      * Returns the {@link Roo.SplitBar} for this region.
32237      * @return {Roo.SplitBar}
32238      */
32239     getSplitBar : function(){
32240         return this.split;
32241     },
32242     
32243     hide : function(){
32244         this.hideSplitter();
32245         Roo.SplitLayoutRegion.superclass.hide.call(this);
32246     },
32247
32248     hideSplitter : function(){
32249         if(this.split){
32250             this.split.el.setLocation(-2000,-2000);
32251             this.split.el.hide();
32252         }
32253     },
32254
32255     show : function(){
32256         if(this.split){
32257             this.split.el.show();
32258         }
32259         Roo.SplitLayoutRegion.superclass.show.call(this);
32260     },
32261     
32262     beforeSlide: function(){
32263         if(Roo.isGecko){// firefox overflow auto bug workaround
32264             this.bodyEl.clip();
32265             if(this.tabs) {
32266                 this.tabs.bodyEl.clip();
32267             }
32268             if(this.activePanel){
32269                 this.activePanel.getEl().clip();
32270                 
32271                 if(this.activePanel.beforeSlide){
32272                     this.activePanel.beforeSlide();
32273                 }
32274             }
32275         }
32276     },
32277     
32278     afterSlide : function(){
32279         if(Roo.isGecko){// firefox overflow auto bug workaround
32280             this.bodyEl.unclip();
32281             if(this.tabs) {
32282                 this.tabs.bodyEl.unclip();
32283             }
32284             if(this.activePanel){
32285                 this.activePanel.getEl().unclip();
32286                 if(this.activePanel.afterSlide){
32287                     this.activePanel.afterSlide();
32288                 }
32289             }
32290         }
32291     },
32292
32293     initAutoHide : function(){
32294         if(this.autoHide !== false){
32295             if(!this.autoHideHd){
32296                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32297                 this.autoHideHd = {
32298                     "mouseout": function(e){
32299                         if(!e.within(this.el, true)){
32300                             st.delay(500);
32301                         }
32302                     },
32303                     "mouseover" : function(e){
32304                         st.cancel();
32305                     },
32306                     scope : this
32307                 };
32308             }
32309             this.el.on(this.autoHideHd);
32310         }
32311     },
32312
32313     clearAutoHide : function(){
32314         if(this.autoHide !== false){
32315             this.el.un("mouseout", this.autoHideHd.mouseout);
32316             this.el.un("mouseover", this.autoHideHd.mouseover);
32317         }
32318     },
32319
32320     clearMonitor : function(){
32321         Roo.get(document).un("click", this.slideInIf, this);
32322     },
32323
32324     // these names are backwards but not changed for compat
32325     slideOut : function(){
32326         if(this.isSlid || this.el.hasActiveFx()){
32327             return;
32328         }
32329         this.isSlid = true;
32330         if(this.collapseBtn){
32331             this.collapseBtn.hide();
32332         }
32333         this.closeBtnState = this.closeBtn.getStyle('display');
32334         this.closeBtn.hide();
32335         if(this.stickBtn){
32336             this.stickBtn.show();
32337         }
32338         this.el.show();
32339         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32340         this.beforeSlide();
32341         this.el.setStyle("z-index", 10001);
32342         this.el.slideIn(this.getSlideAnchor(), {
32343             callback: function(){
32344                 this.afterSlide();
32345                 this.initAutoHide();
32346                 Roo.get(document).on("click", this.slideInIf, this);
32347                 this.fireEvent("slideshow", this);
32348             },
32349             scope: this,
32350             block: true
32351         });
32352     },
32353
32354     afterSlideIn : function(){
32355         this.clearAutoHide();
32356         this.isSlid = false;
32357         this.clearMonitor();
32358         this.el.setStyle("z-index", "");
32359         if(this.collapseBtn){
32360             this.collapseBtn.show();
32361         }
32362         this.closeBtn.setStyle('display', this.closeBtnState);
32363         if(this.stickBtn){
32364             this.stickBtn.hide();
32365         }
32366         this.fireEvent("slidehide", this);
32367     },
32368
32369     slideIn : function(cb){
32370         if(!this.isSlid || this.el.hasActiveFx()){
32371             Roo.callback(cb);
32372             return;
32373         }
32374         this.isSlid = false;
32375         this.beforeSlide();
32376         this.el.slideOut(this.getSlideAnchor(), {
32377             callback: function(){
32378                 this.el.setLeftTop(-10000, -10000);
32379                 this.afterSlide();
32380                 this.afterSlideIn();
32381                 Roo.callback(cb);
32382             },
32383             scope: this,
32384             block: true
32385         });
32386     },
32387     
32388     slideInIf : function(e){
32389         if(!e.within(this.el)){
32390             this.slideIn();
32391         }
32392     },
32393
32394     animateCollapse : function(){
32395         this.beforeSlide();
32396         this.el.setStyle("z-index", 20000);
32397         var anchor = this.getSlideAnchor();
32398         this.el.slideOut(anchor, {
32399             callback : function(){
32400                 this.el.setStyle("z-index", "");
32401                 this.collapsedEl.slideIn(anchor, {duration:.3});
32402                 this.afterSlide();
32403                 this.el.setLocation(-10000,-10000);
32404                 this.el.hide();
32405                 this.fireEvent("collapsed", this);
32406             },
32407             scope: this,
32408             block: true
32409         });
32410     },
32411
32412     animateExpand : function(){
32413         this.beforeSlide();
32414         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32415         this.el.setStyle("z-index", 20000);
32416         this.collapsedEl.hide({
32417             duration:.1
32418         });
32419         this.el.slideIn(this.getSlideAnchor(), {
32420             callback : function(){
32421                 this.el.setStyle("z-index", "");
32422                 this.afterSlide();
32423                 if(this.split){
32424                     this.split.el.show();
32425                 }
32426                 this.fireEvent("invalidated", this);
32427                 this.fireEvent("expanded", this);
32428             },
32429             scope: this,
32430             block: true
32431         });
32432     },
32433
32434     anchors : {
32435         "west" : "left",
32436         "east" : "right",
32437         "north" : "top",
32438         "south" : "bottom"
32439     },
32440
32441     sanchors : {
32442         "west" : "l",
32443         "east" : "r",
32444         "north" : "t",
32445         "south" : "b"
32446     },
32447
32448     canchors : {
32449         "west" : "tl-tr",
32450         "east" : "tr-tl",
32451         "north" : "tl-bl",
32452         "south" : "bl-tl"
32453     },
32454
32455     getAnchor : function(){
32456         return this.anchors[this.position];
32457     },
32458
32459     getCollapseAnchor : function(){
32460         return this.canchors[this.position];
32461     },
32462
32463     getSlideAnchor : function(){
32464         return this.sanchors[this.position];
32465     },
32466
32467     getAlignAdj : function(){
32468         var cm = this.cmargins;
32469         switch(this.position){
32470             case "west":
32471                 return [0, 0];
32472             break;
32473             case "east":
32474                 return [0, 0];
32475             break;
32476             case "north":
32477                 return [0, 0];
32478             break;
32479             case "south":
32480                 return [0, 0];
32481             break;
32482         }
32483     },
32484
32485     getExpandAdj : function(){
32486         var c = this.collapsedEl, cm = this.cmargins;
32487         switch(this.position){
32488             case "west":
32489                 return [-(cm.right+c.getWidth()+cm.left), 0];
32490             break;
32491             case "east":
32492                 return [cm.right+c.getWidth()+cm.left, 0];
32493             break;
32494             case "north":
32495                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32496             break;
32497             case "south":
32498                 return [0, cm.top+cm.bottom+c.getHeight()];
32499             break;
32500         }
32501     }
32502 });/*
32503  * Based on:
32504  * Ext JS Library 1.1.1
32505  * Copyright(c) 2006-2007, Ext JS, LLC.
32506  *
32507  * Originally Released Under LGPL - original licence link has changed is not relivant.
32508  *
32509  * Fork - LGPL
32510  * <script type="text/javascript">
32511  */
32512 /*
32513  * These classes are private internal classes
32514  */
32515 Roo.CenterLayoutRegion = function(mgr, config){
32516     Roo.LayoutRegion.call(this, mgr, config, "center");
32517     this.visible = true;
32518     this.minWidth = config.minWidth || 20;
32519     this.minHeight = config.minHeight || 20;
32520 };
32521
32522 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32523     hide : function(){
32524         // center panel can't be hidden
32525     },
32526     
32527     show : function(){
32528         // center panel can't be hidden
32529     },
32530     
32531     getMinWidth: function(){
32532         return this.minWidth;
32533     },
32534     
32535     getMinHeight: function(){
32536         return this.minHeight;
32537     }
32538 });
32539
32540
32541 Roo.NorthLayoutRegion = function(mgr, config){
32542     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32543     if(this.split){
32544         this.split.placement = Roo.SplitBar.TOP;
32545         this.split.orientation = Roo.SplitBar.VERTICAL;
32546         this.split.el.addClass("x-layout-split-v");
32547     }
32548     var size = config.initialSize || config.height;
32549     if(typeof size != "undefined"){
32550         this.el.setHeight(size);
32551     }
32552 };
32553 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32554     orientation: Roo.SplitBar.VERTICAL,
32555     getBox : function(){
32556         if(this.collapsed){
32557             return this.collapsedEl.getBox();
32558         }
32559         var box = this.el.getBox();
32560         if(this.split){
32561             box.height += this.split.el.getHeight();
32562         }
32563         return box;
32564     },
32565     
32566     updateBox : function(box){
32567         if(this.split && !this.collapsed){
32568             box.height -= this.split.el.getHeight();
32569             this.split.el.setLeft(box.x);
32570             this.split.el.setTop(box.y+box.height);
32571             this.split.el.setWidth(box.width);
32572         }
32573         if(this.collapsed){
32574             this.updateBody(box.width, null);
32575         }
32576         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32577     }
32578 });
32579
32580 Roo.SouthLayoutRegion = function(mgr, config){
32581     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32582     if(this.split){
32583         this.split.placement = Roo.SplitBar.BOTTOM;
32584         this.split.orientation = Roo.SplitBar.VERTICAL;
32585         this.split.el.addClass("x-layout-split-v");
32586     }
32587     var size = config.initialSize || config.height;
32588     if(typeof size != "undefined"){
32589         this.el.setHeight(size);
32590     }
32591 };
32592 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32593     orientation: Roo.SplitBar.VERTICAL,
32594     getBox : function(){
32595         if(this.collapsed){
32596             return this.collapsedEl.getBox();
32597         }
32598         var box = this.el.getBox();
32599         if(this.split){
32600             var sh = this.split.el.getHeight();
32601             box.height += sh;
32602             box.y -= sh;
32603         }
32604         return box;
32605     },
32606     
32607     updateBox : function(box){
32608         if(this.split && !this.collapsed){
32609             var sh = this.split.el.getHeight();
32610             box.height -= sh;
32611             box.y += sh;
32612             this.split.el.setLeft(box.x);
32613             this.split.el.setTop(box.y-sh);
32614             this.split.el.setWidth(box.width);
32615         }
32616         if(this.collapsed){
32617             this.updateBody(box.width, null);
32618         }
32619         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32620     }
32621 });
32622
32623 Roo.EastLayoutRegion = function(mgr, config){
32624     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32625     if(this.split){
32626         this.split.placement = Roo.SplitBar.RIGHT;
32627         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32628         this.split.el.addClass("x-layout-split-h");
32629     }
32630     var size = config.initialSize || config.width;
32631     if(typeof size != "undefined"){
32632         this.el.setWidth(size);
32633     }
32634 };
32635 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32636     orientation: Roo.SplitBar.HORIZONTAL,
32637     getBox : function(){
32638         if(this.collapsed){
32639             return this.collapsedEl.getBox();
32640         }
32641         var box = this.el.getBox();
32642         if(this.split){
32643             var sw = this.split.el.getWidth();
32644             box.width += sw;
32645             box.x -= sw;
32646         }
32647         return box;
32648     },
32649
32650     updateBox : function(box){
32651         if(this.split && !this.collapsed){
32652             var sw = this.split.el.getWidth();
32653             box.width -= sw;
32654             this.split.el.setLeft(box.x);
32655             this.split.el.setTop(box.y);
32656             this.split.el.setHeight(box.height);
32657             box.x += sw;
32658         }
32659         if(this.collapsed){
32660             this.updateBody(null, box.height);
32661         }
32662         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32663     }
32664 });
32665
32666 Roo.WestLayoutRegion = function(mgr, config){
32667     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32668     if(this.split){
32669         this.split.placement = Roo.SplitBar.LEFT;
32670         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32671         this.split.el.addClass("x-layout-split-h");
32672     }
32673     var size = config.initialSize || config.width;
32674     if(typeof size != "undefined"){
32675         this.el.setWidth(size);
32676     }
32677 };
32678 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32679     orientation: Roo.SplitBar.HORIZONTAL,
32680     getBox : function(){
32681         if(this.collapsed){
32682             return this.collapsedEl.getBox();
32683         }
32684         var box = this.el.getBox();
32685         if(this.split){
32686             box.width += this.split.el.getWidth();
32687         }
32688         return box;
32689     },
32690     
32691     updateBox : function(box){
32692         if(this.split && !this.collapsed){
32693             var sw = this.split.el.getWidth();
32694             box.width -= sw;
32695             this.split.el.setLeft(box.x+box.width);
32696             this.split.el.setTop(box.y);
32697             this.split.el.setHeight(box.height);
32698         }
32699         if(this.collapsed){
32700             this.updateBody(null, box.height);
32701         }
32702         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32703     }
32704 });
32705 /*
32706  * Based on:
32707  * Ext JS Library 1.1.1
32708  * Copyright(c) 2006-2007, Ext JS, LLC.
32709  *
32710  * Originally Released Under LGPL - original licence link has changed is not relivant.
32711  *
32712  * Fork - LGPL
32713  * <script type="text/javascript">
32714  */
32715  
32716  
32717 /*
32718  * Private internal class for reading and applying state
32719  */
32720 Roo.LayoutStateManager = function(layout){
32721      // default empty state
32722      this.state = {
32723         north: {},
32724         south: {},
32725         east: {},
32726         west: {}       
32727     };
32728 };
32729
32730 Roo.LayoutStateManager.prototype = {
32731     init : function(layout, provider){
32732         this.provider = provider;
32733         var state = provider.get(layout.id+"-layout-state");
32734         if(state){
32735             var wasUpdating = layout.isUpdating();
32736             if(!wasUpdating){
32737                 layout.beginUpdate();
32738             }
32739             for(var key in state){
32740                 if(typeof state[key] != "function"){
32741                     var rstate = state[key];
32742                     var r = layout.getRegion(key);
32743                     if(r && rstate){
32744                         if(rstate.size){
32745                             r.resizeTo(rstate.size);
32746                         }
32747                         if(rstate.collapsed == true){
32748                             r.collapse(true);
32749                         }else{
32750                             r.expand(null, true);
32751                         }
32752                     }
32753                 }
32754             }
32755             if(!wasUpdating){
32756                 layout.endUpdate();
32757             }
32758             this.state = state; 
32759         }
32760         this.layout = layout;
32761         layout.on("regionresized", this.onRegionResized, this);
32762         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32763         layout.on("regionexpanded", this.onRegionExpanded, this);
32764     },
32765     
32766     storeState : function(){
32767         this.provider.set(this.layout.id+"-layout-state", this.state);
32768     },
32769     
32770     onRegionResized : function(region, newSize){
32771         this.state[region.getPosition()].size = newSize;
32772         this.storeState();
32773     },
32774     
32775     onRegionCollapsed : function(region){
32776         this.state[region.getPosition()].collapsed = true;
32777         this.storeState();
32778     },
32779     
32780     onRegionExpanded : function(region){
32781         this.state[region.getPosition()].collapsed = false;
32782         this.storeState();
32783     }
32784 };/*
32785  * Based on:
32786  * Ext JS Library 1.1.1
32787  * Copyright(c) 2006-2007, Ext JS, LLC.
32788  *
32789  * Originally Released Under LGPL - original licence link has changed is not relivant.
32790  *
32791  * Fork - LGPL
32792  * <script type="text/javascript">
32793  */
32794 /**
32795  * @class Roo.ContentPanel
32796  * @extends Roo.util.Observable
32797  * @children Roo.form.Form Roo.JsonView Roo.View
32798  * @parent Roo.BorderLayout Roo.LayoutDialog builder
32799  * A basic ContentPanel element.
32800  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32801  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32802  * @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
32803  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32804  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32805  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32806  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
32807  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32808  * @cfg {String} title          The title for this panel
32809  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32810  * @cfg {String} url            Calls {@link #setUrl} with this value
32811  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
32812  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32813  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32814  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32815  * @cfg {String}    style  Extra style to add to the content panel
32816  * @cfg {Roo.menu.Menu} menu  popup menu
32817
32818  * @constructor
32819  * Create a new ContentPanel.
32820  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32821  * @param {String/Object} config A string to set only the title or a config object
32822  * @param {String} content (optional) Set the HTML content for this panel
32823  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32824  */
32825 Roo.ContentPanel = function(el, config, content){
32826     
32827      
32828     /*
32829     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32830         config = el;
32831         el = Roo.id();
32832     }
32833     if (config && config.parentLayout) { 
32834         el = config.parentLayout.el.createChild(); 
32835     }
32836     */
32837     if(el.autoCreate){ // xtype is available if this is called from factory
32838         config = el;
32839         el = Roo.id();
32840     }
32841     this.el = Roo.get(el);
32842     if(!this.el && config && config.autoCreate){
32843         if(typeof config.autoCreate == "object"){
32844             if(!config.autoCreate.id){
32845                 config.autoCreate.id = config.id||el;
32846             }
32847             this.el = Roo.DomHelper.append(document.body,
32848                         config.autoCreate, true);
32849         }else{
32850             this.el = Roo.DomHelper.append(document.body,
32851                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32852         }
32853     }
32854     
32855     
32856     this.closable = false;
32857     this.loaded = false;
32858     this.active = false;
32859     if(typeof config == "string"){
32860         this.title = config;
32861     }else{
32862         Roo.apply(this, config);
32863     }
32864     
32865     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32866         this.wrapEl = this.el.wrap();
32867         this.toolbar.container = this.el.insertSibling(false, 'before');
32868         this.toolbar = new Roo.Toolbar(this.toolbar);
32869     }
32870     
32871     // xtype created footer. - not sure if will work as we normally have to render first..
32872     if (this.footer && !this.footer.el && this.footer.xtype) {
32873         if (!this.wrapEl) {
32874             this.wrapEl = this.el.wrap();
32875         }
32876     
32877         this.footer.container = this.wrapEl.createChild();
32878          
32879         this.footer = Roo.factory(this.footer, Roo);
32880         
32881     }
32882     
32883     if(this.resizeEl){
32884         this.resizeEl = Roo.get(this.resizeEl, true);
32885     }else{
32886         this.resizeEl = this.el;
32887     }
32888     // handle view.xtype
32889     
32890  
32891     
32892     
32893     this.addEvents({
32894         /**
32895          * @event activate
32896          * Fires when this panel is activated. 
32897          * @param {Roo.ContentPanel} this
32898          */
32899         "activate" : true,
32900         /**
32901          * @event deactivate
32902          * Fires when this panel is activated. 
32903          * @param {Roo.ContentPanel} this
32904          */
32905         "deactivate" : true,
32906
32907         /**
32908          * @event resize
32909          * Fires when this panel is resized if fitToFrame is true.
32910          * @param {Roo.ContentPanel} this
32911          * @param {Number} width The width after any component adjustments
32912          * @param {Number} height The height after any component adjustments
32913          */
32914         "resize" : true,
32915         
32916          /**
32917          * @event render
32918          * Fires when this tab is created
32919          * @param {Roo.ContentPanel} this
32920          */
32921         "render" : true
32922          
32923         
32924     });
32925     
32926
32927     
32928     
32929     if(this.autoScroll){
32930         this.resizeEl.setStyle("overflow", "auto");
32931     } else {
32932         // fix randome scrolling
32933         this.el.on('scroll', function() {
32934             Roo.log('fix random scolling');
32935             this.scrollTo('top',0); 
32936         });
32937     }
32938     content = content || this.content;
32939     if(content){
32940         this.setContent(content);
32941     }
32942     if(config && config.url){
32943         this.setUrl(this.url, this.params, this.loadOnce);
32944     }
32945     
32946     
32947     
32948     Roo.ContentPanel.superclass.constructor.call(this);
32949     
32950     if (this.view && typeof(this.view.xtype) != 'undefined') {
32951         this.view.el = this.el.appendChild(document.createElement("div"));
32952         this.view = Roo.factory(this.view); 
32953         this.view.render  &&  this.view.render(false, '');  
32954     }
32955     
32956     
32957     this.fireEvent('render', this);
32958 };
32959
32960 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32961     tabTip:'',
32962     setRegion : function(region){
32963         this.region = region;
32964         if(region){
32965            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32966         }else{
32967            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32968         } 
32969     },
32970     
32971     /**
32972      * Returns the toolbar for this Panel if one was configured. 
32973      * @return {Roo.Toolbar} 
32974      */
32975     getToolbar : function(){
32976         return this.toolbar;
32977     },
32978     
32979     setActiveState : function(active){
32980         this.active = active;
32981         if(!active){
32982             this.fireEvent("deactivate", this);
32983         }else{
32984             this.fireEvent("activate", this);
32985         }
32986     },
32987     /**
32988      * Updates this panel's element
32989      * @param {String} content The new content
32990      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32991     */
32992     setContent : function(content, loadScripts){
32993         this.el.update(content, loadScripts);
32994     },
32995
32996     ignoreResize : function(w, h){
32997         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32998             return true;
32999         }else{
33000             this.lastSize = {width: w, height: h};
33001             return false;
33002         }
33003     },
33004     /**
33005      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33006      * @return {Roo.UpdateManager} The UpdateManager
33007      */
33008     getUpdateManager : function(){
33009         return this.el.getUpdateManager();
33010     },
33011      /**
33012      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33013      * @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:
33014 <pre><code>
33015 panel.load({
33016     url: "your-url.php",
33017     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33018     callback: yourFunction,
33019     scope: yourObject, //(optional scope)
33020     discardUrl: false,
33021     nocache: false,
33022     text: "Loading...",
33023     timeout: 30,
33024     scripts: false
33025 });
33026 </code></pre>
33027      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33028      * 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.
33029      * @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}
33030      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33031      * @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.
33032      * @return {Roo.ContentPanel} this
33033      */
33034     load : function(){
33035         var um = this.el.getUpdateManager();
33036         um.update.apply(um, arguments);
33037         return this;
33038     },
33039
33040
33041     /**
33042      * 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.
33043      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33044      * @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)
33045      * @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)
33046      * @return {Roo.UpdateManager} The UpdateManager
33047      */
33048     setUrl : function(url, params, loadOnce){
33049         if(this.refreshDelegate){
33050             this.removeListener("activate", this.refreshDelegate);
33051         }
33052         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33053         this.on("activate", this.refreshDelegate);
33054         return this.el.getUpdateManager();
33055     },
33056     
33057     _handleRefresh : function(url, params, loadOnce){
33058         if(!loadOnce || !this.loaded){
33059             var updater = this.el.getUpdateManager();
33060             updater.update(url, params, this._setLoaded.createDelegate(this));
33061         }
33062     },
33063     
33064     _setLoaded : function(){
33065         this.loaded = true;
33066     }, 
33067     
33068     /**
33069      * Returns this panel's id
33070      * @return {String} 
33071      */
33072     getId : function(){
33073         return this.el.id;
33074     },
33075     
33076     /** 
33077      * Returns this panel's element - used by regiosn to add.
33078      * @return {Roo.Element} 
33079      */
33080     getEl : function(){
33081         return this.wrapEl || this.el;
33082     },
33083     
33084     adjustForComponents : function(width, height)
33085     {
33086         //Roo.log('adjustForComponents ');
33087         if(this.resizeEl != this.el){
33088             width -= this.el.getFrameWidth('lr');
33089             height -= this.el.getFrameWidth('tb');
33090         }
33091         if(this.toolbar){
33092             var te = this.toolbar.getEl();
33093             height -= te.getHeight();
33094             te.setWidth(width);
33095         }
33096         if(this.footer){
33097             var te = this.footer.getEl();
33098             //Roo.log("footer:" + te.getHeight());
33099             
33100             height -= te.getHeight();
33101             te.setWidth(width);
33102         }
33103         
33104         
33105         if(this.adjustments){
33106             width += this.adjustments[0];
33107             height += this.adjustments[1];
33108         }
33109         return {"width": width, "height": height};
33110     },
33111     
33112     setSize : function(width, height){
33113         if(this.fitToFrame && !this.ignoreResize(width, height)){
33114             if(this.fitContainer && this.resizeEl != this.el){
33115                 this.el.setSize(width, height);
33116             }
33117             var size = this.adjustForComponents(width, height);
33118             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33119             this.fireEvent('resize', this, size.width, size.height);
33120         }
33121     },
33122     
33123     /**
33124      * Returns this panel's title
33125      * @return {String} 
33126      */
33127     getTitle : function(){
33128         return this.title;
33129     },
33130     
33131     /**
33132      * Set this panel's title
33133      * @param {String} title
33134      */
33135     setTitle : function(title){
33136         this.title = title;
33137         if(this.region){
33138             this.region.updatePanelTitle(this, title);
33139         }
33140     },
33141     
33142     /**
33143      * Returns true is this panel was configured to be closable
33144      * @return {Boolean} 
33145      */
33146     isClosable : function(){
33147         return this.closable;
33148     },
33149     
33150     beforeSlide : function(){
33151         this.el.clip();
33152         this.resizeEl.clip();
33153     },
33154     
33155     afterSlide : function(){
33156         this.el.unclip();
33157         this.resizeEl.unclip();
33158     },
33159     
33160     /**
33161      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33162      *   Will fail silently if the {@link #setUrl} method has not been called.
33163      *   This does not activate the panel, just updates its content.
33164      */
33165     refresh : function(){
33166         if(this.refreshDelegate){
33167            this.loaded = false;
33168            this.refreshDelegate();
33169         }
33170     },
33171     
33172     /**
33173      * Destroys this panel
33174      */
33175     destroy : function(){
33176         this.el.removeAllListeners();
33177         var tempEl = document.createElement("span");
33178         tempEl.appendChild(this.el.dom);
33179         tempEl.innerHTML = "";
33180         this.el.remove();
33181         this.el = null;
33182     },
33183     
33184     /**
33185      * form - if the content panel contains a form - this is a reference to it.
33186      * @type {Roo.form.Form}
33187      */
33188     form : false,
33189     /**
33190      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33191      *    This contains a reference to it.
33192      * @type {Roo.View}
33193      */
33194     view : false,
33195     
33196       /**
33197      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33198      * <pre><code>
33199
33200 layout.addxtype({
33201        xtype : 'Form',
33202        items: [ .... ]
33203    }
33204 );
33205
33206 </code></pre>
33207      * @param {Object} cfg Xtype definition of item to add.
33208      */
33209     
33210     addxtype : function(cfg) {
33211         // add form..
33212         if (cfg.xtype.match(/^Form$/)) {
33213             
33214             var el;
33215             //if (this.footer) {
33216             //    el = this.footer.container.insertSibling(false, 'before');
33217             //} else {
33218                 el = this.el.createChild();
33219             //}
33220
33221             this.form = new  Roo.form.Form(cfg);
33222             
33223             
33224             if ( this.form.allItems.length) {
33225                 this.form.render(el.dom);
33226             }
33227             return this.form;
33228         }
33229         // should only have one of theses..
33230         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33231             // views.. should not be just added - used named prop 'view''
33232             
33233             cfg.el = this.el.appendChild(document.createElement("div"));
33234             // factory?
33235             
33236             var ret = new Roo.factory(cfg);
33237              
33238              ret.render && ret.render(false, ''); // render blank..
33239             this.view = ret;
33240             return ret;
33241         }
33242         return false;
33243     }
33244 });
33245
33246 /**
33247  * @class Roo.GridPanel
33248  * @extends Roo.ContentPanel
33249  * @parent Roo.BorderLayout Roo.LayoutDialog builder
33250  * @constructor
33251  * Create a new GridPanel.
33252  * @cfg {Roo.grid.Grid} grid The grid for this panel
33253  */
33254 Roo.GridPanel = function(grid, config){
33255     
33256     // universal ctor...
33257     if (typeof(grid.grid) != 'undefined') {
33258         config = grid;
33259         grid = config.grid;
33260     }
33261     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33262         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33263         
33264     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33265     
33266     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33267     
33268     if(this.toolbar){
33269         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33270     }
33271     // xtype created footer. - not sure if will work as we normally have to render first..
33272     if (this.footer && !this.footer.el && this.footer.xtype) {
33273         
33274         this.footer.container = this.grid.getView().getFooterPanel(true);
33275         this.footer.dataSource = this.grid.dataSource;
33276         this.footer = Roo.factory(this.footer, Roo);
33277         
33278     }
33279     
33280     grid.monitorWindowResize = false; // turn off autosizing
33281     grid.autoHeight = false;
33282     grid.autoWidth = false;
33283     this.grid = grid;
33284     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33285 };
33286
33287 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33288     getId : function(){
33289         return this.grid.id;
33290     },
33291     
33292     /**
33293      * Returns the grid for this panel
33294      * @return {Roo.grid.Grid} 
33295      */
33296     getGrid : function(){
33297         return this.grid;    
33298     },
33299     
33300     setSize : function(width, height){
33301         if(!this.ignoreResize(width, height)){
33302             var grid = this.grid;
33303             var size = this.adjustForComponents(width, height);
33304             grid.getGridEl().setSize(size.width, size.height);
33305             grid.autoSize();
33306         }
33307     },
33308     
33309     beforeSlide : function(){
33310         this.grid.getView().scroller.clip();
33311     },
33312     
33313     afterSlide : function(){
33314         this.grid.getView().scroller.unclip();
33315     },
33316     
33317     destroy : function(){
33318         this.grid.destroy();
33319         delete this.grid;
33320         Roo.GridPanel.superclass.destroy.call(this); 
33321     }
33322 });
33323
33324
33325 /**
33326  * @class Roo.NestedLayoutPanel
33327  * @extends Roo.ContentPanel
33328  * @parent Roo.BorderLayout Roo.LayoutDialog builder
33329  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
33330  *
33331  * 
33332  * @constructor
33333  * Create a new NestedLayoutPanel.
33334  * 
33335  * 
33336  * @param {Roo.BorderLayout} layout [required] The layout for this panel
33337  * @param {String/Object} config A string to set only the title or a config object
33338  */
33339 Roo.NestedLayoutPanel = function(layout, config)
33340 {
33341     // construct with only one argument..
33342     /* FIXME - implement nicer consturctors
33343     if (layout.layout) {
33344         config = layout;
33345         layout = config.layout;
33346         delete config.layout;
33347     }
33348     if (layout.xtype && !layout.getEl) {
33349         // then layout needs constructing..
33350         layout = Roo.factory(layout, Roo);
33351     }
33352     */
33353     
33354     
33355     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33356     
33357     layout.monitorWindowResize = false; // turn off autosizing
33358     this.layout = layout;
33359     this.layout.getEl().addClass("x-layout-nested-layout");
33360     
33361     
33362     
33363     
33364 };
33365
33366 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33367
33368     setSize : function(width, height){
33369         if(!this.ignoreResize(width, height)){
33370             var size = this.adjustForComponents(width, height);
33371             var el = this.layout.getEl();
33372             el.setSize(size.width, size.height);
33373             var touch = el.dom.offsetWidth;
33374             this.layout.layout();
33375             // ie requires a double layout on the first pass
33376             if(Roo.isIE && !this.initialized){
33377                 this.initialized = true;
33378                 this.layout.layout();
33379             }
33380         }
33381     },
33382     
33383     // activate all subpanels if not currently active..
33384     
33385     setActiveState : function(active){
33386         this.active = active;
33387         if(!active){
33388             this.fireEvent("deactivate", this);
33389             return;
33390         }
33391         
33392         this.fireEvent("activate", this);
33393         // not sure if this should happen before or after..
33394         if (!this.layout) {
33395             return; // should not happen..
33396         }
33397         var reg = false;
33398         for (var r in this.layout.regions) {
33399             reg = this.layout.getRegion(r);
33400             if (reg.getActivePanel()) {
33401                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33402                 reg.setActivePanel(reg.getActivePanel());
33403                 continue;
33404             }
33405             if (!reg.panels.length) {
33406                 continue;
33407             }
33408             reg.showPanel(reg.getPanel(0));
33409         }
33410         
33411         
33412         
33413         
33414     },
33415     
33416     /**
33417      * Returns the nested BorderLayout for this panel
33418      * @return {Roo.BorderLayout} 
33419      */
33420     getLayout : function(){
33421         return this.layout;
33422     },
33423     
33424      /**
33425      * Adds a xtype elements to the layout of the nested panel
33426      * <pre><code>
33427
33428 panel.addxtype({
33429        xtype : 'ContentPanel',
33430        region: 'west',
33431        items: [ .... ]
33432    }
33433 );
33434
33435 panel.addxtype({
33436         xtype : 'NestedLayoutPanel',
33437         region: 'west',
33438         layout: {
33439            center: { },
33440            west: { }   
33441         },
33442         items : [ ... list of content panels or nested layout panels.. ]
33443    }
33444 );
33445 </code></pre>
33446      * @param {Object} cfg Xtype definition of item to add.
33447      */
33448     addxtype : function(cfg) {
33449         return this.layout.addxtype(cfg);
33450     
33451     }
33452 });
33453
33454 Roo.ScrollPanel = function(el, config, content){
33455     config = config || {};
33456     config.fitToFrame = true;
33457     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33458     
33459     this.el.dom.style.overflow = "hidden";
33460     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33461     this.el.removeClass("x-layout-inactive-content");
33462     this.el.on("mousewheel", this.onWheel, this);
33463
33464     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33465     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33466     up.unselectable(); down.unselectable();
33467     up.on("click", this.scrollUp, this);
33468     down.on("click", this.scrollDown, this);
33469     up.addClassOnOver("x-scroller-btn-over");
33470     down.addClassOnOver("x-scroller-btn-over");
33471     up.addClassOnClick("x-scroller-btn-click");
33472     down.addClassOnClick("x-scroller-btn-click");
33473     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33474
33475     this.resizeEl = this.el;
33476     this.el = wrap; this.up = up; this.down = down;
33477 };
33478
33479 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33480     increment : 100,
33481     wheelIncrement : 5,
33482     scrollUp : function(){
33483         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33484     },
33485
33486     scrollDown : function(){
33487         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33488     },
33489
33490     afterScroll : function(){
33491         var el = this.resizeEl;
33492         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33493         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33494         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33495     },
33496
33497     setSize : function(){
33498         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33499         this.afterScroll();
33500     },
33501
33502     onWheel : function(e){
33503         var d = e.getWheelDelta();
33504         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33505         this.afterScroll();
33506         e.stopEvent();
33507     },
33508
33509     setContent : function(content, loadScripts){
33510         this.resizeEl.update(content, loadScripts);
33511     }
33512
33513 });
33514
33515
33516
33517 /**
33518  * @class Roo.TreePanel
33519  * @extends Roo.ContentPanel
33520  * @parent Roo.BorderLayout Roo.LayoutDialog builder
33521  * Treepanel component
33522  * 
33523  * @constructor
33524  * Create a new TreePanel. - defaults to fit/scoll contents.
33525  * @param {String/Object} config A string to set only the panel's title, or a config object
33526  */
33527 Roo.TreePanel = function(config){
33528     var el = config.el;
33529     var tree = config.tree;
33530     delete config.tree; 
33531     delete config.el; // hopefull!
33532     
33533     // wrapper for IE7 strict & safari scroll issue
33534     
33535     var treeEl = el.createChild();
33536     config.resizeEl = treeEl;
33537     
33538     
33539     
33540     Roo.TreePanel.superclass.constructor.call(this, el, config);
33541  
33542  
33543     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33544     //console.log(tree);
33545     this.on('activate', function()
33546     {
33547         if (this.tree.rendered) {
33548             return;
33549         }
33550         //console.log('render tree');
33551         this.tree.render();
33552     });
33553     // this should not be needed.. - it's actually the 'el' that resizes?
33554     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
33555     
33556     //this.on('resize',  function (cp, w, h) {
33557     //        this.tree.innerCt.setWidth(w);
33558     //        this.tree.innerCt.setHeight(h);
33559     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
33560     //});
33561
33562         
33563     
33564 };
33565
33566 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33567     fitToFrame : true,
33568     autoScroll : true,
33569     /*
33570      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
33571      */
33572     tree : false
33573
33574 });
33575
33576
33577
33578
33579
33580
33581
33582
33583
33584
33585
33586 /*
33587  * Based on:
33588  * Ext JS Library 1.1.1
33589  * Copyright(c) 2006-2007, Ext JS, LLC.
33590  *
33591  * Originally Released Under LGPL - original licence link has changed is not relivant.
33592  *
33593  * Fork - LGPL
33594  * <script type="text/javascript">
33595  */
33596  
33597
33598 /**
33599  * @class Roo.ReaderLayout
33600  * @extends Roo.BorderLayout
33601  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33602  * center region containing two nested regions (a top one for a list view and one for item preview below),
33603  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33604  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33605  * expedites the setup of the overall layout and regions for this common application style.
33606  * Example:
33607  <pre><code>
33608 var reader = new Roo.ReaderLayout();
33609 var CP = Roo.ContentPanel;  // shortcut for adding
33610
33611 reader.beginUpdate();
33612 reader.add("north", new CP("north", "North"));
33613 reader.add("west", new CP("west", {title: "West"}));
33614 reader.add("east", new CP("east", {title: "East"}));
33615
33616 reader.regions.listView.add(new CP("listView", "List"));
33617 reader.regions.preview.add(new CP("preview", "Preview"));
33618 reader.endUpdate();
33619 </code></pre>
33620 * @constructor
33621 * Create a new ReaderLayout
33622 * @param {Object} config Configuration options
33623 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33624 * document.body if omitted)
33625 */
33626 Roo.ReaderLayout = function(config, renderTo){
33627     var c = config || {size:{}};
33628     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33629         north: c.north !== false ? Roo.apply({
33630             split:false,
33631             initialSize: 32,
33632             titlebar: false
33633         }, c.north) : false,
33634         west: c.west !== false ? Roo.apply({
33635             split:true,
33636             initialSize: 200,
33637             minSize: 175,
33638             maxSize: 400,
33639             titlebar: true,
33640             collapsible: true,
33641             animate: true,
33642             margins:{left:5,right:0,bottom:5,top:5},
33643             cmargins:{left:5,right:5,bottom:5,top:5}
33644         }, c.west) : false,
33645         east: c.east !== false ? Roo.apply({
33646             split:true,
33647             initialSize: 200,
33648             minSize: 175,
33649             maxSize: 400,
33650             titlebar: true,
33651             collapsible: true,
33652             animate: true,
33653             margins:{left:0,right:5,bottom:5,top:5},
33654             cmargins:{left:5,right:5,bottom:5,top:5}
33655         }, c.east) : false,
33656         center: Roo.apply({
33657             tabPosition: 'top',
33658             autoScroll:false,
33659             closeOnTab: true,
33660             titlebar:false,
33661             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33662         }, c.center)
33663     });
33664
33665     this.el.addClass('x-reader');
33666
33667     this.beginUpdate();
33668
33669     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33670         south: c.preview !== false ? Roo.apply({
33671             split:true,
33672             initialSize: 200,
33673             minSize: 100,
33674             autoScroll:true,
33675             collapsible:true,
33676             titlebar: true,
33677             cmargins:{top:5,left:0, right:0, bottom:0}
33678         }, c.preview) : false,
33679         center: Roo.apply({
33680             autoScroll:false,
33681             titlebar:false,
33682             minHeight:200
33683         }, c.listView)
33684     });
33685     this.add('center', new Roo.NestedLayoutPanel(inner,
33686             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33687
33688     this.endUpdate();
33689
33690     this.regions.preview = inner.getRegion('south');
33691     this.regions.listView = inner.getRegion('center');
33692 };
33693
33694 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33695  * Based on:
33696  * Ext JS Library 1.1.1
33697  * Copyright(c) 2006-2007, Ext JS, LLC.
33698  *
33699  * Originally Released Under LGPL - original licence link has changed is not relivant.
33700  *
33701  * Fork - LGPL
33702  * <script type="text/javascript">
33703  */
33704  
33705 /**
33706  * @class Roo.grid.Grid
33707  * @extends Roo.util.Observable
33708  * This class represents the primary interface of a component based grid control.
33709  * <br><br>Usage:<pre><code>
33710  var grid = new Roo.grid.Grid("my-container-id", {
33711      ds: myDataStore,
33712      cm: myColModel,
33713      selModel: mySelectionModel,
33714      autoSizeColumns: true,
33715      monitorWindowResize: false,
33716      trackMouseOver: true
33717  });
33718  // set any options
33719  grid.render();
33720  * </code></pre>
33721  * <b>Common Problems:</b><br/>
33722  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33723  * element will correct this<br/>
33724  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33725  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33726  * are unpredictable.<br/>
33727  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33728  * grid to calculate dimensions/offsets.<br/>
33729   * @constructor
33730  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33731  * The container MUST have some type of size defined for the grid to fill. The container will be
33732  * automatically set to position relative if it isn't already.
33733  * @param {Object} config A config object that sets properties on this grid.
33734  */
33735 Roo.grid.Grid = function(container, config){
33736         // initialize the container
33737         this.container = Roo.get(container);
33738         this.container.update("");
33739         this.container.setStyle("overflow", "hidden");
33740     this.container.addClass('x-grid-container');
33741
33742     this.id = this.container.id;
33743
33744     Roo.apply(this, config);
33745     // check and correct shorthanded configs
33746     if(this.ds){
33747         this.dataSource = this.ds;
33748         delete this.ds;
33749     }
33750     if(this.cm){
33751         this.colModel = this.cm;
33752         delete this.cm;
33753     }
33754     if(this.sm){
33755         this.selModel = this.sm;
33756         delete this.sm;
33757     }
33758
33759     if (this.selModel) {
33760         this.selModel = Roo.factory(this.selModel, Roo.grid);
33761         this.sm = this.selModel;
33762         this.sm.xmodule = this.xmodule || false;
33763     }
33764     if (typeof(this.colModel.config) == 'undefined') {
33765         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33766         this.cm = this.colModel;
33767         this.cm.xmodule = this.xmodule || false;
33768     }
33769     if (this.dataSource) {
33770         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33771         this.ds = this.dataSource;
33772         this.ds.xmodule = this.xmodule || false;
33773          
33774     }
33775     
33776     
33777     
33778     if(this.width){
33779         this.container.setWidth(this.width);
33780     }
33781
33782     if(this.height){
33783         this.container.setHeight(this.height);
33784     }
33785     /** @private */
33786         this.addEvents({
33787         // raw events
33788         /**
33789          * @event click
33790          * The raw click event for the entire grid.
33791          * @param {Roo.EventObject} e
33792          */
33793         "click" : true,
33794         /**
33795          * @event dblclick
33796          * The raw dblclick event for the entire grid.
33797          * @param {Roo.EventObject} e
33798          */
33799         "dblclick" : true,
33800         /**
33801          * @event contextmenu
33802          * The raw contextmenu event for the entire grid.
33803          * @param {Roo.EventObject} e
33804          */
33805         "contextmenu" : true,
33806         /**
33807          * @event mousedown
33808          * The raw mousedown event for the entire grid.
33809          * @param {Roo.EventObject} e
33810          */
33811         "mousedown" : true,
33812         /**
33813          * @event mouseup
33814          * The raw mouseup event for the entire grid.
33815          * @param {Roo.EventObject} e
33816          */
33817         "mouseup" : true,
33818         /**
33819          * @event mouseover
33820          * The raw mouseover event for the entire grid.
33821          * @param {Roo.EventObject} e
33822          */
33823         "mouseover" : true,
33824         /**
33825          * @event mouseout
33826          * The raw mouseout event for the entire grid.
33827          * @param {Roo.EventObject} e
33828          */
33829         "mouseout" : true,
33830         /**
33831          * @event keypress
33832          * The raw keypress event for the entire grid.
33833          * @param {Roo.EventObject} e
33834          */
33835         "keypress" : true,
33836         /**
33837          * @event keydown
33838          * The raw keydown event for the entire grid.
33839          * @param {Roo.EventObject} e
33840          */
33841         "keydown" : true,
33842
33843         // custom events
33844
33845         /**
33846          * @event cellclick
33847          * Fires when a cell is clicked
33848          * @param {Grid} this
33849          * @param {Number} rowIndex
33850          * @param {Number} columnIndex
33851          * @param {Roo.EventObject} e
33852          */
33853         "cellclick" : true,
33854         /**
33855          * @event celldblclick
33856          * Fires when a cell is double clicked
33857          * @param {Grid} this
33858          * @param {Number} rowIndex
33859          * @param {Number} columnIndex
33860          * @param {Roo.EventObject} e
33861          */
33862         "celldblclick" : true,
33863         /**
33864          * @event rowclick
33865          * Fires when a row is clicked
33866          * @param {Grid} this
33867          * @param {Number} rowIndex
33868          * @param {Roo.EventObject} e
33869          */
33870         "rowclick" : true,
33871         /**
33872          * @event rowdblclick
33873          * Fires when a row is double clicked
33874          * @param {Grid} this
33875          * @param {Number} rowIndex
33876          * @param {Roo.EventObject} e
33877          */
33878         "rowdblclick" : true,
33879         /**
33880          * @event headerclick
33881          * Fires when a header is clicked
33882          * @param {Grid} this
33883          * @param {Number} columnIndex
33884          * @param {Roo.EventObject} e
33885          */
33886         "headerclick" : true,
33887         /**
33888          * @event headerdblclick
33889          * Fires when a header cell is double clicked
33890          * @param {Grid} this
33891          * @param {Number} columnIndex
33892          * @param {Roo.EventObject} e
33893          */
33894         "headerdblclick" : true,
33895         /**
33896          * @event rowcontextmenu
33897          * Fires when a row is right clicked
33898          * @param {Grid} this
33899          * @param {Number} rowIndex
33900          * @param {Roo.EventObject} e
33901          */
33902         "rowcontextmenu" : true,
33903         /**
33904          * @event cellcontextmenu
33905          * Fires when a cell is right clicked
33906          * @param {Grid} this
33907          * @param {Number} rowIndex
33908          * @param {Number} cellIndex
33909          * @param {Roo.EventObject} e
33910          */
33911          "cellcontextmenu" : true,
33912         /**
33913          * @event headercontextmenu
33914          * Fires when a header is right clicked
33915          * @param {Grid} this
33916          * @param {Number} columnIndex
33917          * @param {Roo.EventObject} e
33918          */
33919         "headercontextmenu" : true,
33920         /**
33921          * @event bodyscroll
33922          * Fires when the body element is scrolled
33923          * @param {Number} scrollLeft
33924          * @param {Number} scrollTop
33925          */
33926         "bodyscroll" : true,
33927         /**
33928          * @event columnresize
33929          * Fires when the user resizes a column
33930          * @param {Number} columnIndex
33931          * @param {Number} newSize
33932          */
33933         "columnresize" : true,
33934         /**
33935          * @event columnmove
33936          * Fires when the user moves a column
33937          * @param {Number} oldIndex
33938          * @param {Number} newIndex
33939          */
33940         "columnmove" : true,
33941         /**
33942          * @event startdrag
33943          * Fires when row(s) start being dragged
33944          * @param {Grid} this
33945          * @param {Roo.GridDD} dd The drag drop object
33946          * @param {event} e The raw browser event
33947          */
33948         "startdrag" : true,
33949         /**
33950          * @event enddrag
33951          * Fires when a drag operation is complete
33952          * @param {Grid} this
33953          * @param {Roo.GridDD} dd The drag drop object
33954          * @param {event} e The raw browser event
33955          */
33956         "enddrag" : true,
33957         /**
33958          * @event dragdrop
33959          * Fires when dragged row(s) are dropped on a valid DD target
33960          * @param {Grid} this
33961          * @param {Roo.GridDD} dd The drag drop object
33962          * @param {String} targetId The target drag drop object
33963          * @param {event} e The raw browser event
33964          */
33965         "dragdrop" : true,
33966         /**
33967          * @event dragover
33968          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33969          * @param {Grid} this
33970          * @param {Roo.GridDD} dd The drag drop object
33971          * @param {String} targetId The target drag drop object
33972          * @param {event} e The raw browser event
33973          */
33974         "dragover" : true,
33975         /**
33976          * @event dragenter
33977          *  Fires when the dragged row(s) first cross another DD target while being dragged
33978          * @param {Grid} this
33979          * @param {Roo.GridDD} dd The drag drop object
33980          * @param {String} targetId The target drag drop object
33981          * @param {event} e The raw browser event
33982          */
33983         "dragenter" : true,
33984         /**
33985          * @event dragout
33986          * Fires when the dragged row(s) leave another DD target while being dragged
33987          * @param {Grid} this
33988          * @param {Roo.GridDD} dd The drag drop object
33989          * @param {String} targetId The target drag drop object
33990          * @param {event} e The raw browser event
33991          */
33992         "dragout" : true,
33993         /**
33994          * @event rowclass
33995          * Fires when a row is rendered, so you can change add a style to it.
33996          * @param {GridView} gridview   The grid view
33997          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33998          */
33999         'rowclass' : true,
34000
34001         /**
34002          * @event render
34003          * Fires when the grid is rendered
34004          * @param {Grid} grid
34005          */
34006         'render' : true
34007     });
34008
34009     Roo.grid.Grid.superclass.constructor.call(this);
34010 };
34011 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
34012     
34013     /**
34014          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
34015          */
34016         /**
34017          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
34018          */
34019         /**
34020          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
34021          */
34022         /**
34023          * @cfg {Roo.grid.Store} ds The data store for the grid
34024          */
34025         /**
34026          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
34027          */
34028         /**
34029      * @cfg {String} ddGroup - drag drop group.
34030      */
34031       /**
34032      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
34033      */
34034
34035     /**
34036      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
34037      */
34038     minColumnWidth : 25,
34039
34040     /**
34041      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
34042      * <b>on initial render.</b> It is more efficient to explicitly size the columns
34043      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
34044      */
34045     autoSizeColumns : false,
34046
34047     /**
34048      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
34049      */
34050     autoSizeHeaders : true,
34051
34052     /**
34053      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
34054      */
34055     monitorWindowResize : true,
34056
34057     /**
34058      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34059      * rows measured to get a columns size. Default is 0 (all rows).
34060      */
34061     maxRowsToMeasure : 0,
34062
34063     /**
34064      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34065      */
34066     trackMouseOver : true,
34067
34068     /**
34069     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34070     */
34071       /**
34072     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
34073     */
34074     
34075     /**
34076     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34077     */
34078     enableDragDrop : false,
34079     
34080     /**
34081     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34082     */
34083     enableColumnMove : true,
34084     
34085     /**
34086     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34087     */
34088     enableColumnHide : true,
34089     
34090     /**
34091     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34092     */
34093     enableRowHeightSync : false,
34094     
34095     /**
34096     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34097     */
34098     stripeRows : true,
34099     
34100     /**
34101     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34102     */
34103     autoHeight : false,
34104
34105     /**
34106      * @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.
34107      */
34108     autoExpandColumn : false,
34109
34110     /**
34111     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34112     * Default is 50.
34113     */
34114     autoExpandMin : 50,
34115
34116     /**
34117     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34118     */
34119     autoExpandMax : 1000,
34120
34121     /**
34122     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34123     */
34124     view : null,
34125
34126     /**
34127     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34128     */
34129     loadMask : false,
34130     /**
34131     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
34132     */
34133     dropTarget: false,
34134     
34135    
34136     
34137     // private
34138     rendered : false,
34139
34140     /**
34141     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34142     * of a fixed width. Default is false.
34143     */
34144     /**
34145     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34146     */
34147     
34148     
34149     /**
34150     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34151     * %0 is replaced with the number of selected rows.
34152     */
34153     ddText : "{0} selected row{1}",
34154     
34155     
34156     /**
34157      * Called once after all setup has been completed and the grid is ready to be rendered.
34158      * @return {Roo.grid.Grid} this
34159      */
34160     render : function()
34161     {
34162         var c = this.container;
34163         // try to detect autoHeight/width mode
34164         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34165             this.autoHeight = true;
34166         }
34167         var view = this.getView();
34168         view.init(this);
34169
34170         c.on("click", this.onClick, this);
34171         c.on("dblclick", this.onDblClick, this);
34172         c.on("contextmenu", this.onContextMenu, this);
34173         c.on("keydown", this.onKeyDown, this);
34174         if (Roo.isTouch) {
34175             c.on("touchstart", this.onTouchStart, this);
34176         }
34177
34178         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34179
34180         this.getSelectionModel().init(this);
34181
34182         view.render();
34183
34184         if(this.loadMask){
34185             this.loadMask = new Roo.LoadMask(this.container,
34186                     Roo.apply({store:this.dataSource}, this.loadMask));
34187         }
34188         
34189         
34190         if (this.toolbar && this.toolbar.xtype) {
34191             this.toolbar.container = this.getView().getHeaderPanel(true);
34192             this.toolbar = new Roo.Toolbar(this.toolbar);
34193         }
34194         if (this.footer && this.footer.xtype) {
34195             this.footer.dataSource = this.getDataSource();
34196             this.footer.container = this.getView().getFooterPanel(true);
34197             this.footer = Roo.factory(this.footer, Roo);
34198         }
34199         if (this.dropTarget && this.dropTarget.xtype) {
34200             delete this.dropTarget.xtype;
34201             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34202         }
34203         
34204         
34205         this.rendered = true;
34206         this.fireEvent('render', this);
34207         return this;
34208     },
34209
34210     /**
34211      * Reconfigures the grid to use a different Store and Column Model.
34212      * The View will be bound to the new objects and refreshed.
34213      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34214      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34215      */
34216     reconfigure : function(dataSource, colModel){
34217         if(this.loadMask){
34218             this.loadMask.destroy();
34219             this.loadMask = new Roo.LoadMask(this.container,
34220                     Roo.apply({store:dataSource}, this.loadMask));
34221         }
34222         this.view.bind(dataSource, colModel);
34223         this.dataSource = dataSource;
34224         this.colModel = colModel;
34225         this.view.refresh(true);
34226     },
34227     /**
34228      * addColumns
34229      * Add's a column, default at the end..
34230      
34231      * @param {int} position to add (default end)
34232      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
34233      */
34234     addColumns : function(pos, ar)
34235     {
34236         
34237         for (var i =0;i< ar.length;i++) {
34238             var cfg = ar[i];
34239             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
34240             this.cm.lookup[cfg.id] = cfg;
34241         }
34242         
34243         
34244         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
34245             pos = this.cm.config.length; //this.cm.config.push(cfg);
34246         } 
34247         pos = Math.max(0,pos);
34248         ar.unshift(0);
34249         ar.unshift(pos);
34250         this.cm.config.splice.apply(this.cm.config, ar);
34251         
34252         
34253         
34254         this.view.generateRules(this.cm);
34255         this.view.refresh(true);
34256         
34257     },
34258     
34259     
34260     
34261     
34262     // private
34263     onKeyDown : function(e){
34264         this.fireEvent("keydown", e);
34265     },
34266
34267     /**
34268      * Destroy this grid.
34269      * @param {Boolean} removeEl True to remove the element
34270      */
34271     destroy : function(removeEl, keepListeners){
34272         if(this.loadMask){
34273             this.loadMask.destroy();
34274         }
34275         var c = this.container;
34276         c.removeAllListeners();
34277         this.view.destroy();
34278         this.colModel.purgeListeners();
34279         if(!keepListeners){
34280             this.purgeListeners();
34281         }
34282         c.update("");
34283         if(removeEl === true){
34284             c.remove();
34285         }
34286     },
34287
34288     // private
34289     processEvent : function(name, e){
34290         // does this fire select???
34291         //Roo.log('grid:processEvent '  + name);
34292         
34293         if (name != 'touchstart' ) {
34294             this.fireEvent(name, e);    
34295         }
34296         
34297         var t = e.getTarget();
34298         var v = this.view;
34299         var header = v.findHeaderIndex(t);
34300         if(header !== false){
34301             var ename = name == 'touchstart' ? 'click' : name;
34302              
34303             this.fireEvent("header" + ename, this, header, e);
34304         }else{
34305             var row = v.findRowIndex(t);
34306             var cell = v.findCellIndex(t);
34307             if (name == 'touchstart') {
34308                 // first touch is always a click.
34309                 // hopefull this happens after selection is updated.?
34310                 name = false;
34311                 
34312                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
34313                     var cs = this.selModel.getSelectedCell();
34314                     if (row == cs[0] && cell == cs[1]){
34315                         name = 'dblclick';
34316                     }
34317                 }
34318                 if (typeof(this.selModel.getSelections) != 'undefined') {
34319                     var cs = this.selModel.getSelections();
34320                     var ds = this.dataSource;
34321                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
34322                         name = 'dblclick';
34323                     }
34324                 }
34325                 if (!name) {
34326                     return;
34327                 }
34328             }
34329             
34330             
34331             if(row !== false){
34332                 this.fireEvent("row" + name, this, row, e);
34333                 if(cell !== false){
34334                     this.fireEvent("cell" + name, this, row, cell, e);
34335                 }
34336             }
34337         }
34338     },
34339
34340     // private
34341     onClick : function(e){
34342         this.processEvent("click", e);
34343     },
34344    // private
34345     onTouchStart : function(e){
34346         this.processEvent("touchstart", e);
34347     },
34348
34349     // private
34350     onContextMenu : function(e, t){
34351         this.processEvent("contextmenu", e);
34352     },
34353
34354     // private
34355     onDblClick : function(e){
34356         this.processEvent("dblclick", e);
34357     },
34358
34359     // private
34360     walkCells : function(row, col, step, fn, scope){
34361         var cm = this.colModel, clen = cm.getColumnCount();
34362         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34363         if(step < 0){
34364             if(col < 0){
34365                 row--;
34366                 first = false;
34367             }
34368             while(row >= 0){
34369                 if(!first){
34370                     col = clen-1;
34371                 }
34372                 first = false;
34373                 while(col >= 0){
34374                     if(fn.call(scope || this, row, col, cm) === true){
34375                         return [row, col];
34376                     }
34377                     col--;
34378                 }
34379                 row--;
34380             }
34381         } else {
34382             if(col >= clen){
34383                 row++;
34384                 first = false;
34385             }
34386             while(row < rlen){
34387                 if(!first){
34388                     col = 0;
34389                 }
34390                 first = false;
34391                 while(col < clen){
34392                     if(fn.call(scope || this, row, col, cm) === true){
34393                         return [row, col];
34394                     }
34395                     col++;
34396                 }
34397                 row++;
34398             }
34399         }
34400         return null;
34401     },
34402
34403     // private
34404     getSelections : function(){
34405         return this.selModel.getSelections();
34406     },
34407
34408     /**
34409      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34410      * but if manual update is required this method will initiate it.
34411      */
34412     autoSize : function(){
34413         if(this.rendered){
34414             this.view.layout();
34415             if(this.view.adjustForScroll){
34416                 this.view.adjustForScroll();
34417             }
34418         }
34419     },
34420
34421     /**
34422      * Returns the grid's underlying element.
34423      * @return {Element} The element
34424      */
34425     getGridEl : function(){
34426         return this.container;
34427     },
34428
34429     // private for compatibility, overridden by editor grid
34430     stopEditing : function(){},
34431
34432     /**
34433      * Returns the grid's SelectionModel.
34434      * @return {SelectionModel}
34435      */
34436     getSelectionModel : function(){
34437         if(!this.selModel){
34438             this.selModel = new Roo.grid.RowSelectionModel();
34439         }
34440         return this.selModel;
34441     },
34442
34443     /**
34444      * Returns the grid's DataSource.
34445      * @return {DataSource}
34446      */
34447     getDataSource : function(){
34448         return this.dataSource;
34449     },
34450
34451     /**
34452      * Returns the grid's ColumnModel.
34453      * @return {ColumnModel}
34454      */
34455     getColumnModel : function(){
34456         return this.colModel;
34457     },
34458
34459     /**
34460      * Returns the grid's GridView object.
34461      * @return {GridView}
34462      */
34463     getView : function(){
34464         if(!this.view){
34465             this.view = new Roo.grid.GridView(this.viewConfig);
34466             this.relayEvents(this.view, [
34467                 "beforerowremoved", "beforerowsinserted",
34468                 "beforerefresh", "rowremoved",
34469                 "rowsinserted", "rowupdated" ,"refresh"
34470             ]);
34471         }
34472         return this.view;
34473     },
34474     /**
34475      * Called to get grid's drag proxy text, by default returns this.ddText.
34476      * Override this to put something different in the dragged text.
34477      * @return {String}
34478      */
34479     getDragDropText : function(){
34480         var count = this.selModel.getCount();
34481         return String.format(this.ddText, count, count == 1 ? '' : 's');
34482     }
34483 });
34484 /*
34485  * Based on:
34486  * Ext JS Library 1.1.1
34487  * Copyright(c) 2006-2007, Ext JS, LLC.
34488  *
34489  * Originally Released Under LGPL - original licence link has changed is not relivant.
34490  *
34491  * Fork - LGPL
34492  * <script type="text/javascript">
34493  */
34494  /**
34495  * @class Roo.grid.AbstractGridView
34496  * @extends Roo.util.Observable
34497  * @abstract
34498  * Abstract base class for grid Views
34499  * @constructor
34500  */
34501 Roo.grid.AbstractGridView = function(){
34502         this.grid = null;
34503         
34504         this.events = {
34505             "beforerowremoved" : true,
34506             "beforerowsinserted" : true,
34507             "beforerefresh" : true,
34508             "rowremoved" : true,
34509             "rowsinserted" : true,
34510             "rowupdated" : true,
34511             "refresh" : true
34512         };
34513     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34514 };
34515
34516 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34517     rowClass : "x-grid-row",
34518     cellClass : "x-grid-cell",
34519     tdClass : "x-grid-td",
34520     hdClass : "x-grid-hd",
34521     splitClass : "x-grid-hd-split",
34522     
34523     init: function(grid){
34524         this.grid = grid;
34525                 var cid = this.grid.getGridEl().id;
34526         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34527         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34528         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34529         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34530         },
34531         
34532     getColumnRenderers : function(){
34533         var renderers = [];
34534         var cm = this.grid.colModel;
34535         var colCount = cm.getColumnCount();
34536         for(var i = 0; i < colCount; i++){
34537             renderers[i] = cm.getRenderer(i);
34538         }
34539         return renderers;
34540     },
34541     
34542     getColumnIds : function(){
34543         var ids = [];
34544         var cm = this.grid.colModel;
34545         var colCount = cm.getColumnCount();
34546         for(var i = 0; i < colCount; i++){
34547             ids[i] = cm.getColumnId(i);
34548         }
34549         return ids;
34550     },
34551     
34552     getDataIndexes : function(){
34553         if(!this.indexMap){
34554             this.indexMap = this.buildIndexMap();
34555         }
34556         return this.indexMap.colToData;
34557     },
34558     
34559     getColumnIndexByDataIndex : function(dataIndex){
34560         if(!this.indexMap){
34561             this.indexMap = this.buildIndexMap();
34562         }
34563         return this.indexMap.dataToCol[dataIndex];
34564     },
34565     
34566     /**
34567      * Set a css style for a column dynamically. 
34568      * @param {Number} colIndex The index of the column
34569      * @param {String} name The css property name
34570      * @param {String} value The css value
34571      */
34572     setCSSStyle : function(colIndex, name, value){
34573         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34574         Roo.util.CSS.updateRule(selector, name, value);
34575     },
34576     
34577     generateRules : function(cm){
34578         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34579         Roo.util.CSS.removeStyleSheet(rulesId);
34580         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34581             var cid = cm.getColumnId(i);
34582             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34583                          this.tdSelector, cid, " {\n}\n",
34584                          this.hdSelector, cid, " {\n}\n",
34585                          this.splitSelector, cid, " {\n}\n");
34586         }
34587         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34588     }
34589 });/*
34590  * Based on:
34591  * Ext JS Library 1.1.1
34592  * Copyright(c) 2006-2007, Ext JS, LLC.
34593  *
34594  * Originally Released Under LGPL - original licence link has changed is not relivant.
34595  *
34596  * Fork - LGPL
34597  * <script type="text/javascript">
34598  */
34599
34600 // private
34601 // This is a support class used internally by the Grid components
34602 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34603     this.grid = grid;
34604     this.view = grid.getView();
34605     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34606     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34607     if(hd2){
34608         this.setHandleElId(Roo.id(hd));
34609         this.setOuterHandleElId(Roo.id(hd2));
34610     }
34611     this.scroll = false;
34612 };
34613 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34614     maxDragWidth: 120,
34615     getDragData : function(e){
34616         var t = Roo.lib.Event.getTarget(e);
34617         var h = this.view.findHeaderCell(t);
34618         if(h){
34619             return {ddel: h.firstChild, header:h};
34620         }
34621         return false;
34622     },
34623
34624     onInitDrag : function(e){
34625         this.view.headersDisabled = true;
34626         var clone = this.dragData.ddel.cloneNode(true);
34627         clone.id = Roo.id();
34628         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34629         this.proxy.update(clone);
34630         return true;
34631     },
34632
34633     afterValidDrop : function(){
34634         var v = this.view;
34635         setTimeout(function(){
34636             v.headersDisabled = false;
34637         }, 50);
34638     },
34639
34640     afterInvalidDrop : function(){
34641         var v = this.view;
34642         setTimeout(function(){
34643             v.headersDisabled = false;
34644         }, 50);
34645     }
34646 });
34647 /*
34648  * Based on:
34649  * Ext JS Library 1.1.1
34650  * Copyright(c) 2006-2007, Ext JS, LLC.
34651  *
34652  * Originally Released Under LGPL - original licence link has changed is not relivant.
34653  *
34654  * Fork - LGPL
34655  * <script type="text/javascript">
34656  */
34657 // private
34658 // This is a support class used internally by the Grid components
34659 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34660     this.grid = grid;
34661     this.view = grid.getView();
34662     // split the proxies so they don't interfere with mouse events
34663     this.proxyTop = Roo.DomHelper.append(document.body, {
34664         cls:"col-move-top", html:"&#160;"
34665     }, true);
34666     this.proxyBottom = Roo.DomHelper.append(document.body, {
34667         cls:"col-move-bottom", html:"&#160;"
34668     }, true);
34669     this.proxyTop.hide = this.proxyBottom.hide = function(){
34670         this.setLeftTop(-100,-100);
34671         this.setStyle("visibility", "hidden");
34672     };
34673     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34674     // temporarily disabled
34675     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34676     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34677 };
34678 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34679     proxyOffsets : [-4, -9],
34680     fly: Roo.Element.fly,
34681
34682     getTargetFromEvent : function(e){
34683         var t = Roo.lib.Event.getTarget(e);
34684         var cindex = this.view.findCellIndex(t);
34685         if(cindex !== false){
34686             return this.view.getHeaderCell(cindex);
34687         }
34688         return null;
34689     },
34690
34691     nextVisible : function(h){
34692         var v = this.view, cm = this.grid.colModel;
34693         h = h.nextSibling;
34694         while(h){
34695             if(!cm.isHidden(v.getCellIndex(h))){
34696                 return h;
34697             }
34698             h = h.nextSibling;
34699         }
34700         return null;
34701     },
34702
34703     prevVisible : function(h){
34704         var v = this.view, cm = this.grid.colModel;
34705         h = h.prevSibling;
34706         while(h){
34707             if(!cm.isHidden(v.getCellIndex(h))){
34708                 return h;
34709             }
34710             h = h.prevSibling;
34711         }
34712         return null;
34713     },
34714
34715     positionIndicator : function(h, n, e){
34716         var x = Roo.lib.Event.getPageX(e);
34717         var r = Roo.lib.Dom.getRegion(n.firstChild);
34718         var px, pt, py = r.top + this.proxyOffsets[1];
34719         if((r.right - x) <= (r.right-r.left)/2){
34720             px = r.right+this.view.borderWidth;
34721             pt = "after";
34722         }else{
34723             px = r.left;
34724             pt = "before";
34725         }
34726         var oldIndex = this.view.getCellIndex(h);
34727         var newIndex = this.view.getCellIndex(n);
34728
34729         if(this.grid.colModel.isFixed(newIndex)){
34730             return false;
34731         }
34732
34733         var locked = this.grid.colModel.isLocked(newIndex);
34734
34735         if(pt == "after"){
34736             newIndex++;
34737         }
34738         if(oldIndex < newIndex){
34739             newIndex--;
34740         }
34741         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34742             return false;
34743         }
34744         px +=  this.proxyOffsets[0];
34745         this.proxyTop.setLeftTop(px, py);
34746         this.proxyTop.show();
34747         if(!this.bottomOffset){
34748             this.bottomOffset = this.view.mainHd.getHeight();
34749         }
34750         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34751         this.proxyBottom.show();
34752         return pt;
34753     },
34754
34755     onNodeEnter : function(n, dd, e, data){
34756         if(data.header != n){
34757             this.positionIndicator(data.header, n, e);
34758         }
34759     },
34760
34761     onNodeOver : function(n, dd, e, data){
34762         var result = false;
34763         if(data.header != n){
34764             result = this.positionIndicator(data.header, n, e);
34765         }
34766         if(!result){
34767             this.proxyTop.hide();
34768             this.proxyBottom.hide();
34769         }
34770         return result ? this.dropAllowed : this.dropNotAllowed;
34771     },
34772
34773     onNodeOut : function(n, dd, e, data){
34774         this.proxyTop.hide();
34775         this.proxyBottom.hide();
34776     },
34777
34778     onNodeDrop : function(n, dd, e, data){
34779         var h = data.header;
34780         if(h != n){
34781             var cm = this.grid.colModel;
34782             var x = Roo.lib.Event.getPageX(e);
34783             var r = Roo.lib.Dom.getRegion(n.firstChild);
34784             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34785             var oldIndex = this.view.getCellIndex(h);
34786             var newIndex = this.view.getCellIndex(n);
34787             var locked = cm.isLocked(newIndex);
34788             if(pt == "after"){
34789                 newIndex++;
34790             }
34791             if(oldIndex < newIndex){
34792                 newIndex--;
34793             }
34794             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34795                 return false;
34796             }
34797             cm.setLocked(oldIndex, locked, true);
34798             cm.moveColumn(oldIndex, newIndex);
34799             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34800             return true;
34801         }
34802         return false;
34803     }
34804 });
34805 /*
34806  * Based on:
34807  * Ext JS Library 1.1.1
34808  * Copyright(c) 2006-2007, Ext JS, LLC.
34809  *
34810  * Originally Released Under LGPL - original licence link has changed is not relivant.
34811  *
34812  * Fork - LGPL
34813  * <script type="text/javascript">
34814  */
34815   
34816 /**
34817  * @class Roo.grid.GridView
34818  * @extends Roo.util.Observable
34819  *
34820  * @constructor
34821  * @param {Object} config
34822  */
34823 Roo.grid.GridView = function(config){
34824     Roo.grid.GridView.superclass.constructor.call(this);
34825     this.el = null;
34826
34827     Roo.apply(this, config);
34828 };
34829
34830 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34831
34832     unselectable :  'unselectable="on"',
34833     unselectableCls :  'x-unselectable',
34834     
34835     
34836     rowClass : "x-grid-row",
34837
34838     cellClass : "x-grid-col",
34839
34840     tdClass : "x-grid-td",
34841
34842     hdClass : "x-grid-hd",
34843
34844     splitClass : "x-grid-split",
34845
34846     sortClasses : ["sort-asc", "sort-desc"],
34847
34848     enableMoveAnim : false,
34849
34850     hlColor: "C3DAF9",
34851
34852     dh : Roo.DomHelper,
34853
34854     fly : Roo.Element.fly,
34855
34856     css : Roo.util.CSS,
34857
34858     borderWidth: 1,
34859
34860     splitOffset: 3,
34861
34862     scrollIncrement : 22,
34863
34864     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34865
34866     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34867
34868     bind : function(ds, cm){
34869         if(this.ds){
34870             this.ds.un("load", this.onLoad, this);
34871             this.ds.un("datachanged", this.onDataChange, this);
34872             this.ds.un("add", this.onAdd, this);
34873             this.ds.un("remove", this.onRemove, this);
34874             this.ds.un("update", this.onUpdate, this);
34875             this.ds.un("clear", this.onClear, this);
34876         }
34877         if(ds){
34878             ds.on("load", this.onLoad, this);
34879             ds.on("datachanged", this.onDataChange, this);
34880             ds.on("add", this.onAdd, this);
34881             ds.on("remove", this.onRemove, this);
34882             ds.on("update", this.onUpdate, this);
34883             ds.on("clear", this.onClear, this);
34884         }
34885         this.ds = ds;
34886
34887         if(this.cm){
34888             this.cm.un("widthchange", this.onColWidthChange, this);
34889             this.cm.un("headerchange", this.onHeaderChange, this);
34890             this.cm.un("hiddenchange", this.onHiddenChange, this);
34891             this.cm.un("columnmoved", this.onColumnMove, this);
34892             this.cm.un("columnlockchange", this.onColumnLock, this);
34893         }
34894         if(cm){
34895             this.generateRules(cm);
34896             cm.on("widthchange", this.onColWidthChange, this);
34897             cm.on("headerchange", this.onHeaderChange, this);
34898             cm.on("hiddenchange", this.onHiddenChange, this);
34899             cm.on("columnmoved", this.onColumnMove, this);
34900             cm.on("columnlockchange", this.onColumnLock, this);
34901         }
34902         this.cm = cm;
34903     },
34904
34905     init: function(grid){
34906         Roo.grid.GridView.superclass.init.call(this, grid);
34907
34908         this.bind(grid.dataSource, grid.colModel);
34909
34910         grid.on("headerclick", this.handleHeaderClick, this);
34911
34912         if(grid.trackMouseOver){
34913             grid.on("mouseover", this.onRowOver, this);
34914             grid.on("mouseout", this.onRowOut, this);
34915         }
34916         grid.cancelTextSelection = function(){};
34917         this.gridId = grid.id;
34918
34919         var tpls = this.templates || {};
34920
34921         if(!tpls.master){
34922             tpls.master = new Roo.Template(
34923                '<div class="x-grid" hidefocus="true">',
34924                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34925                   '<div class="x-grid-topbar"></div>',
34926                   '<div class="x-grid-scroller"><div></div></div>',
34927                   '<div class="x-grid-locked">',
34928                       '<div class="x-grid-header">{lockedHeader}</div>',
34929                       '<div class="x-grid-body">{lockedBody}</div>',
34930                   "</div>",
34931                   '<div class="x-grid-viewport">',
34932                       '<div class="x-grid-header">{header}</div>',
34933                       '<div class="x-grid-body">{body}</div>',
34934                   "</div>",
34935                   '<div class="x-grid-bottombar"></div>',
34936                  
34937                   '<div class="x-grid-resize-proxy">&#160;</div>',
34938                "</div>"
34939             );
34940             tpls.master.disableformats = true;
34941         }
34942
34943         if(!tpls.header){
34944             tpls.header = new Roo.Template(
34945                '<table border="0" cellspacing="0" cellpadding="0">',
34946                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34947                "</table>{splits}"
34948             );
34949             tpls.header.disableformats = true;
34950         }
34951         tpls.header.compile();
34952
34953         if(!tpls.hcell){
34954             tpls.hcell = new Roo.Template(
34955                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34956                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34957                 "</div></td>"
34958              );
34959              tpls.hcell.disableFormats = true;
34960         }
34961         tpls.hcell.compile();
34962
34963         if(!tpls.hsplit){
34964             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
34965                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
34966             tpls.hsplit.disableFormats = true;
34967         }
34968         tpls.hsplit.compile();
34969
34970         if(!tpls.body){
34971             tpls.body = new Roo.Template(
34972                '<table border="0" cellspacing="0" cellpadding="0">',
34973                "<tbody>{rows}</tbody>",
34974                "</table>"
34975             );
34976             tpls.body.disableFormats = true;
34977         }
34978         tpls.body.compile();
34979
34980         if(!tpls.row){
34981             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34982             tpls.row.disableFormats = true;
34983         }
34984         tpls.row.compile();
34985
34986         if(!tpls.cell){
34987             tpls.cell = new Roo.Template(
34988                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34989                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
34990                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
34991                 "</td>"
34992             );
34993             tpls.cell.disableFormats = true;
34994         }
34995         tpls.cell.compile();
34996
34997         this.templates = tpls;
34998     },
34999
35000     // remap these for backwards compat
35001     onColWidthChange : function(){
35002         this.updateColumns.apply(this, arguments);
35003     },
35004     onHeaderChange : function(){
35005         this.updateHeaders.apply(this, arguments);
35006     }, 
35007     onHiddenChange : function(){
35008         this.handleHiddenChange.apply(this, arguments);
35009     },
35010     onColumnMove : function(){
35011         this.handleColumnMove.apply(this, arguments);
35012     },
35013     onColumnLock : function(){
35014         this.handleLockChange.apply(this, arguments);
35015     },
35016
35017     onDataChange : function(){
35018         this.refresh();
35019         this.updateHeaderSortState();
35020     },
35021
35022     onClear : function(){
35023         this.refresh();
35024     },
35025
35026     onUpdate : function(ds, record){
35027         this.refreshRow(record);
35028     },
35029
35030     refreshRow : function(record){
35031         var ds = this.ds, index;
35032         if(typeof record == 'number'){
35033             index = record;
35034             record = ds.getAt(index);
35035         }else{
35036             index = ds.indexOf(record);
35037         }
35038         this.insertRows(ds, index, index, true);
35039         this.onRemove(ds, record, index+1, true);
35040         this.syncRowHeights(index, index);
35041         this.layout();
35042         this.fireEvent("rowupdated", this, index, record);
35043     },
35044
35045     onAdd : function(ds, records, index){
35046         this.insertRows(ds, index, index + (records.length-1));
35047     },
35048
35049     onRemove : function(ds, record, index, isUpdate){
35050         if(isUpdate !== true){
35051             this.fireEvent("beforerowremoved", this, index, record);
35052         }
35053         var bt = this.getBodyTable(), lt = this.getLockedTable();
35054         if(bt.rows[index]){
35055             bt.firstChild.removeChild(bt.rows[index]);
35056         }
35057         if(lt.rows[index]){
35058             lt.firstChild.removeChild(lt.rows[index]);
35059         }
35060         if(isUpdate !== true){
35061             this.stripeRows(index);
35062             this.syncRowHeights(index, index);
35063             this.layout();
35064             this.fireEvent("rowremoved", this, index, record);
35065         }
35066     },
35067
35068     onLoad : function(){
35069         this.scrollToTop();
35070     },
35071
35072     /**
35073      * Scrolls the grid to the top
35074      */
35075     scrollToTop : function(){
35076         if(this.scroller){
35077             this.scroller.dom.scrollTop = 0;
35078             this.syncScroll();
35079         }
35080     },
35081
35082     /**
35083      * Gets a panel in the header of the grid that can be used for toolbars etc.
35084      * After modifying the contents of this panel a call to grid.autoSize() may be
35085      * required to register any changes in size.
35086      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35087      * @return Roo.Element
35088      */
35089     getHeaderPanel : function(doShow){
35090         if(doShow){
35091             this.headerPanel.show();
35092         }
35093         return this.headerPanel;
35094     },
35095
35096     /**
35097      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35098      * After modifying the contents of this panel a call to grid.autoSize() may be
35099      * required to register any changes in size.
35100      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35101      * @return Roo.Element
35102      */
35103     getFooterPanel : function(doShow){
35104         if(doShow){
35105             this.footerPanel.show();
35106         }
35107         return this.footerPanel;
35108     },
35109
35110     initElements : function(){
35111         var E = Roo.Element;
35112         var el = this.grid.getGridEl().dom.firstChild;
35113         var cs = el.childNodes;
35114
35115         this.el = new E(el);
35116         
35117          this.focusEl = new E(el.firstChild);
35118         this.focusEl.swallowEvent("click", true);
35119         
35120         this.headerPanel = new E(cs[1]);
35121         this.headerPanel.enableDisplayMode("block");
35122
35123         this.scroller = new E(cs[2]);
35124         this.scrollSizer = new E(this.scroller.dom.firstChild);
35125
35126         this.lockedWrap = new E(cs[3]);
35127         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35128         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35129
35130         this.mainWrap = new E(cs[4]);
35131         this.mainHd = new E(this.mainWrap.dom.firstChild);
35132         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35133
35134         this.footerPanel = new E(cs[5]);
35135         this.footerPanel.enableDisplayMode("block");
35136
35137         this.resizeProxy = new E(cs[6]);
35138
35139         this.headerSelector = String.format(
35140            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35141            this.lockedHd.id, this.mainHd.id
35142         );
35143
35144         this.splitterSelector = String.format(
35145            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35146            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35147         );
35148     },
35149     idToCssName : function(s)
35150     {
35151         return s.replace(/[^a-z0-9]+/ig, '-');
35152     },
35153
35154     getHeaderCell : function(index){
35155         return Roo.DomQuery.select(this.headerSelector)[index];
35156     },
35157
35158     getHeaderCellMeasure : function(index){
35159         return this.getHeaderCell(index).firstChild;
35160     },
35161
35162     getHeaderCellText : function(index){
35163         return this.getHeaderCell(index).firstChild.firstChild;
35164     },
35165
35166     getLockedTable : function(){
35167         return this.lockedBody.dom.firstChild;
35168     },
35169
35170     getBodyTable : function(){
35171         return this.mainBody.dom.firstChild;
35172     },
35173
35174     getLockedRow : function(index){
35175         return this.getLockedTable().rows[index];
35176     },
35177
35178     getRow : function(index){
35179         return this.getBodyTable().rows[index];
35180     },
35181
35182     getRowComposite : function(index){
35183         if(!this.rowEl){
35184             this.rowEl = new Roo.CompositeElementLite();
35185         }
35186         var els = [], lrow, mrow;
35187         if(lrow = this.getLockedRow(index)){
35188             els.push(lrow);
35189         }
35190         if(mrow = this.getRow(index)){
35191             els.push(mrow);
35192         }
35193         this.rowEl.elements = els;
35194         return this.rowEl;
35195     },
35196     /**
35197      * Gets the 'td' of the cell
35198      * 
35199      * @param {Integer} rowIndex row to select
35200      * @param {Integer} colIndex column to select
35201      * 
35202      * @return {Object} 
35203      */
35204     getCell : function(rowIndex, colIndex){
35205         var locked = this.cm.getLockedCount();
35206         var source;
35207         if(colIndex < locked){
35208             source = this.lockedBody.dom.firstChild;
35209         }else{
35210             source = this.mainBody.dom.firstChild;
35211             colIndex -= locked;
35212         }
35213         return source.rows[rowIndex].childNodes[colIndex];
35214     },
35215
35216     getCellText : function(rowIndex, colIndex){
35217         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35218     },
35219
35220     getCellBox : function(cell){
35221         var b = this.fly(cell).getBox();
35222         if(Roo.isOpera){ // opera fails to report the Y
35223             b.y = cell.offsetTop + this.mainBody.getY();
35224         }
35225         return b;
35226     },
35227
35228     getCellIndex : function(cell){
35229         var id = String(cell.className).match(this.cellRE);
35230         if(id){
35231             return parseInt(id[1], 10);
35232         }
35233         return 0;
35234     },
35235
35236     findHeaderIndex : function(n){
35237         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35238         return r ? this.getCellIndex(r) : false;
35239     },
35240
35241     findHeaderCell : function(n){
35242         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35243         return r ? r : false;
35244     },
35245
35246     findRowIndex : function(n){
35247         if(!n){
35248             return false;
35249         }
35250         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35251         return r ? r.rowIndex : false;
35252     },
35253
35254     findCellIndex : function(node){
35255         var stop = this.el.dom;
35256         while(node && node != stop){
35257             if(this.findRE.test(node.className)){
35258                 return this.getCellIndex(node);
35259             }
35260             node = node.parentNode;
35261         }
35262         return false;
35263     },
35264
35265     getColumnId : function(index){
35266         return this.cm.getColumnId(index);
35267     },
35268
35269     getSplitters : function()
35270     {
35271         if(this.splitterSelector){
35272            return Roo.DomQuery.select(this.splitterSelector);
35273         }else{
35274             return null;
35275       }
35276     },
35277
35278     getSplitter : function(index){
35279         return this.getSplitters()[index];
35280     },
35281
35282     onRowOver : function(e, t){
35283         var row;
35284         if((row = this.findRowIndex(t)) !== false){
35285             this.getRowComposite(row).addClass("x-grid-row-over");
35286         }
35287     },
35288
35289     onRowOut : function(e, t){
35290         var row;
35291         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35292             this.getRowComposite(row).removeClass("x-grid-row-over");
35293         }
35294     },
35295
35296     renderHeaders : function(){
35297         var cm = this.cm;
35298         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35299         var cb = [], lb = [], sb = [], lsb = [], p = {};
35300         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35301             p.cellId = "x-grid-hd-0-" + i;
35302             p.splitId = "x-grid-csplit-0-" + i;
35303             p.id = cm.getColumnId(i);
35304             p.value = cm.getColumnHeader(i) || "";
35305             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
35306             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35307             if(!cm.isLocked(i)){
35308                 cb[cb.length] = ct.apply(p);
35309                 sb[sb.length] = st.apply(p);
35310             }else{
35311                 lb[lb.length] = ct.apply(p);
35312                 lsb[lsb.length] = st.apply(p);
35313             }
35314         }
35315         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35316                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35317     },
35318
35319     updateHeaders : function(){
35320         var html = this.renderHeaders();
35321         this.lockedHd.update(html[0]);
35322         this.mainHd.update(html[1]);
35323     },
35324
35325     /**
35326      * Focuses the specified row.
35327      * @param {Number} row The row index
35328      */
35329     focusRow : function(row)
35330     {
35331         //Roo.log('GridView.focusRow');
35332         var x = this.scroller.dom.scrollLeft;
35333         this.focusCell(row, 0, false);
35334         this.scroller.dom.scrollLeft = x;
35335     },
35336
35337     /**
35338      * Focuses the specified cell.
35339      * @param {Number} row The row index
35340      * @param {Number} col The column index
35341      * @param {Boolean} hscroll false to disable horizontal scrolling
35342      */
35343     focusCell : function(row, col, hscroll)
35344     {
35345         //Roo.log('GridView.focusCell');
35346         var el = this.ensureVisible(row, col, hscroll);
35347         this.focusEl.alignTo(el, "tl-tl");
35348         if(Roo.isGecko){
35349             this.focusEl.focus();
35350         }else{
35351             this.focusEl.focus.defer(1, this.focusEl);
35352         }
35353     },
35354
35355     /**
35356      * Scrolls the specified cell into view
35357      * @param {Number} row The row index
35358      * @param {Number} col The column index
35359      * @param {Boolean} hscroll false to disable horizontal scrolling
35360      */
35361     ensureVisible : function(row, col, hscroll)
35362     {
35363         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35364         //return null; //disable for testing.
35365         if(typeof row != "number"){
35366             row = row.rowIndex;
35367         }
35368         if(row < 0 && row >= this.ds.getCount()){
35369             return  null;
35370         }
35371         col = (col !== undefined ? col : 0);
35372         var cm = this.grid.colModel;
35373         while(cm.isHidden(col)){
35374             col++;
35375         }
35376
35377         var el = this.getCell(row, col);
35378         if(!el){
35379             return null;
35380         }
35381         var c = this.scroller.dom;
35382
35383         var ctop = parseInt(el.offsetTop, 10);
35384         var cleft = parseInt(el.offsetLeft, 10);
35385         var cbot = ctop + el.offsetHeight;
35386         var cright = cleft + el.offsetWidth;
35387         
35388         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35389         var stop = parseInt(c.scrollTop, 10);
35390         var sleft = parseInt(c.scrollLeft, 10);
35391         var sbot = stop + ch;
35392         var sright = sleft + c.clientWidth;
35393         /*
35394         Roo.log('GridView.ensureVisible:' +
35395                 ' ctop:' + ctop +
35396                 ' c.clientHeight:' + c.clientHeight +
35397                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35398                 ' stop:' + stop +
35399                 ' cbot:' + cbot +
35400                 ' sbot:' + sbot +
35401                 ' ch:' + ch  
35402                 );
35403         */
35404         if(ctop < stop){
35405             c.scrollTop = ctop;
35406             //Roo.log("set scrolltop to ctop DISABLE?");
35407         }else if(cbot > sbot){
35408             //Roo.log("set scrolltop to cbot-ch");
35409             c.scrollTop = cbot-ch;
35410         }
35411         
35412         if(hscroll !== false){
35413             if(cleft < sleft){
35414                 c.scrollLeft = cleft;
35415             }else if(cright > sright){
35416                 c.scrollLeft = cright-c.clientWidth;
35417             }
35418         }
35419          
35420         return el;
35421     },
35422
35423     updateColumns : function(){
35424         this.grid.stopEditing();
35425         var cm = this.grid.colModel, colIds = this.getColumnIds();
35426         //var totalWidth = cm.getTotalWidth();
35427         var pos = 0;
35428         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35429             //if(cm.isHidden(i)) continue;
35430             var w = cm.getColumnWidth(i);
35431             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35432             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35433         }
35434         this.updateSplitters();
35435     },
35436
35437     generateRules : function(cm){
35438         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35439         Roo.util.CSS.removeStyleSheet(rulesId);
35440         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35441             var cid = cm.getColumnId(i);
35442             var align = '';
35443             if(cm.config[i].align){
35444                 align = 'text-align:'+cm.config[i].align+';';
35445             }
35446             var hidden = '';
35447             if(cm.isHidden(i)){
35448                 hidden = 'display:none;';
35449             }
35450             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35451             ruleBuf.push(
35452                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35453                     this.hdSelector, cid, " {\n", align, width, "}\n",
35454                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35455                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35456         }
35457         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35458     },
35459
35460     updateSplitters : function(){
35461         var cm = this.cm, s = this.getSplitters();
35462         if(s){ // splitters not created yet
35463             var pos = 0, locked = true;
35464             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35465                 if(cm.isHidden(i)) {
35466                     continue;
35467                 }
35468                 var w = cm.getColumnWidth(i); // make sure it's a number
35469                 if(!cm.isLocked(i) && locked){
35470                     pos = 0;
35471                     locked = false;
35472                 }
35473                 pos += w;
35474                 s[i].style.left = (pos-this.splitOffset) + "px";
35475             }
35476         }
35477     },
35478
35479     handleHiddenChange : function(colModel, colIndex, hidden){
35480         if(hidden){
35481             this.hideColumn(colIndex);
35482         }else{
35483             this.unhideColumn(colIndex);
35484         }
35485     },
35486
35487     hideColumn : function(colIndex){
35488         var cid = this.getColumnId(colIndex);
35489         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35490         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35491         if(Roo.isSafari){
35492             this.updateHeaders();
35493         }
35494         this.updateSplitters();
35495         this.layout();
35496     },
35497
35498     unhideColumn : function(colIndex){
35499         var cid = this.getColumnId(colIndex);
35500         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35501         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35502
35503         if(Roo.isSafari){
35504             this.updateHeaders();
35505         }
35506         this.updateSplitters();
35507         this.layout();
35508     },
35509
35510     insertRows : function(dm, firstRow, lastRow, isUpdate){
35511         if(firstRow == 0 && lastRow == dm.getCount()-1){
35512             this.refresh();
35513         }else{
35514             if(!isUpdate){
35515                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35516             }
35517             var s = this.getScrollState();
35518             var markup = this.renderRows(firstRow, lastRow);
35519             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35520             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35521             this.restoreScroll(s);
35522             if(!isUpdate){
35523                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35524                 this.syncRowHeights(firstRow, lastRow);
35525                 this.stripeRows(firstRow);
35526                 this.layout();
35527             }
35528         }
35529     },
35530
35531     bufferRows : function(markup, target, index){
35532         var before = null, trows = target.rows, tbody = target.tBodies[0];
35533         if(index < trows.length){
35534             before = trows[index];
35535         }
35536         var b = document.createElement("div");
35537         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35538         var rows = b.firstChild.rows;
35539         for(var i = 0, len = rows.length; i < len; i++){
35540             if(before){
35541                 tbody.insertBefore(rows[0], before);
35542             }else{
35543                 tbody.appendChild(rows[0]);
35544             }
35545         }
35546         b.innerHTML = "";
35547         b = null;
35548     },
35549
35550     deleteRows : function(dm, firstRow, lastRow){
35551         if(dm.getRowCount()<1){
35552             this.fireEvent("beforerefresh", this);
35553             this.mainBody.update("");
35554             this.lockedBody.update("");
35555             this.fireEvent("refresh", this);
35556         }else{
35557             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35558             var bt = this.getBodyTable();
35559             var tbody = bt.firstChild;
35560             var rows = bt.rows;
35561             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35562                 tbody.removeChild(rows[firstRow]);
35563             }
35564             this.stripeRows(firstRow);
35565             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35566         }
35567     },
35568
35569     updateRows : function(dataSource, firstRow, lastRow){
35570         var s = this.getScrollState();
35571         this.refresh();
35572         this.restoreScroll(s);
35573     },
35574
35575     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35576         if(!noRefresh){
35577            this.refresh();
35578         }
35579         this.updateHeaderSortState();
35580     },
35581
35582     getScrollState : function(){
35583         
35584         var sb = this.scroller.dom;
35585         return {left: sb.scrollLeft, top: sb.scrollTop};
35586     },
35587
35588     stripeRows : function(startRow){
35589         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35590             return;
35591         }
35592         startRow = startRow || 0;
35593         var rows = this.getBodyTable().rows;
35594         var lrows = this.getLockedTable().rows;
35595         var cls = ' x-grid-row-alt ';
35596         for(var i = startRow, len = rows.length; i < len; i++){
35597             var row = rows[i], lrow = lrows[i];
35598             var isAlt = ((i+1) % 2 == 0);
35599             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35600             if(isAlt == hasAlt){
35601                 continue;
35602             }
35603             if(isAlt){
35604                 row.className += " x-grid-row-alt";
35605             }else{
35606                 row.className = row.className.replace("x-grid-row-alt", "");
35607             }
35608             if(lrow){
35609                 lrow.className = row.className;
35610             }
35611         }
35612     },
35613
35614     restoreScroll : function(state){
35615         //Roo.log('GridView.restoreScroll');
35616         var sb = this.scroller.dom;
35617         sb.scrollLeft = state.left;
35618         sb.scrollTop = state.top;
35619         this.syncScroll();
35620     },
35621
35622     syncScroll : function(){
35623         //Roo.log('GridView.syncScroll');
35624         var sb = this.scroller.dom;
35625         var sh = this.mainHd.dom;
35626         var bs = this.mainBody.dom;
35627         var lv = this.lockedBody.dom;
35628         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35629         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35630     },
35631
35632     handleScroll : function(e){
35633         this.syncScroll();
35634         var sb = this.scroller.dom;
35635         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35636         e.stopEvent();
35637     },
35638
35639     handleWheel : function(e){
35640         var d = e.getWheelDelta();
35641         this.scroller.dom.scrollTop -= d*22;
35642         // set this here to prevent jumpy scrolling on large tables
35643         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35644         e.stopEvent();
35645     },
35646
35647     renderRows : function(startRow, endRow){
35648         // pull in all the crap needed to render rows
35649         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35650         var colCount = cm.getColumnCount();
35651
35652         if(ds.getCount() < 1){
35653             return ["", ""];
35654         }
35655
35656         // build a map for all the columns
35657         var cs = [];
35658         for(var i = 0; i < colCount; i++){
35659             var name = cm.getDataIndex(i);
35660             cs[i] = {
35661                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35662                 renderer : cm.getRenderer(i),
35663                 id : cm.getColumnId(i),
35664                 locked : cm.isLocked(i),
35665                 has_editor : cm.isCellEditable(i)
35666             };
35667         }
35668
35669         startRow = startRow || 0;
35670         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35671
35672         // records to render
35673         var rs = ds.getRange(startRow, endRow);
35674
35675         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35676     },
35677
35678     // As much as I hate to duplicate code, this was branched because FireFox really hates
35679     // [].join("") on strings. The performance difference was substantial enough to
35680     // branch this function
35681     doRender : Roo.isGecko ?
35682             function(cs, rs, ds, startRow, colCount, stripe){
35683                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35684                 // buffers
35685                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35686                 
35687                 var hasListener = this.grid.hasListener('rowclass');
35688                 var rowcfg = {};
35689                 for(var j = 0, len = rs.length; j < len; j++){
35690                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35691                     for(var i = 0; i < colCount; i++){
35692                         c = cs[i];
35693                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35694                         p.id = c.id;
35695                         p.css = p.attr = "";
35696                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35697                         if(p.value == undefined || p.value === "") {
35698                             p.value = "&#160;";
35699                         }
35700                         if(c.has_editor){
35701                             p.css += ' x-grid-editable-cell';
35702                         }
35703                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
35704                             p.css +=  ' x-grid-dirty-cell';
35705                         }
35706                         var markup = ct.apply(p);
35707                         if(!c.locked){
35708                             cb+= markup;
35709                         }else{
35710                             lcb+= markup;
35711                         }
35712                     }
35713                     var alt = [];
35714                     if(stripe && ((rowIndex+1) % 2 == 0)){
35715                         alt.push("x-grid-row-alt")
35716                     }
35717                     if(r.dirty){
35718                         alt.push(  " x-grid-dirty-row");
35719                     }
35720                     rp.cells = lcb;
35721                     if(this.getRowClass){
35722                         alt.push(this.getRowClass(r, rowIndex));
35723                     }
35724                     if (hasListener) {
35725                         rowcfg = {
35726                              
35727                             record: r,
35728                             rowIndex : rowIndex,
35729                             rowClass : ''
35730                         };
35731                         this.grid.fireEvent('rowclass', this, rowcfg);
35732                         alt.push(rowcfg.rowClass);
35733                     }
35734                     rp.alt = alt.join(" ");
35735                     lbuf+= rt.apply(rp);
35736                     rp.cells = cb;
35737                     buf+=  rt.apply(rp);
35738                 }
35739                 return [lbuf, buf];
35740             } :
35741             function(cs, rs, ds, startRow, colCount, stripe){
35742                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35743                 // buffers
35744                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35745                 var hasListener = this.grid.hasListener('rowclass');
35746  
35747                 var rowcfg = {};
35748                 for(var j = 0, len = rs.length; j < len; j++){
35749                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35750                     for(var i = 0; i < colCount; i++){
35751                         c = cs[i];
35752                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35753                         p.id = c.id;
35754                         p.css = p.attr = "";
35755                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35756                         if(p.value == undefined || p.value === "") {
35757                             p.value = "&#160;";
35758                         }
35759                         //Roo.log(c);
35760                          if(c.has_editor){
35761                             p.css += ' x-grid-editable-cell';
35762                         }
35763                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35764                             p.css += ' x-grid-dirty-cell' 
35765                         }
35766                         
35767                         var markup = ct.apply(p);
35768                         if(!c.locked){
35769                             cb[cb.length] = markup;
35770                         }else{
35771                             lcb[lcb.length] = markup;
35772                         }
35773                     }
35774                     var alt = [];
35775                     if(stripe && ((rowIndex+1) % 2 == 0)){
35776                         alt.push( "x-grid-row-alt");
35777                     }
35778                     if(r.dirty){
35779                         alt.push(" x-grid-dirty-row");
35780                     }
35781                     rp.cells = lcb;
35782                     if(this.getRowClass){
35783                         alt.push( this.getRowClass(r, rowIndex));
35784                     }
35785                     if (hasListener) {
35786                         rowcfg = {
35787                              
35788                             record: r,
35789                             rowIndex : rowIndex,
35790                             rowClass : ''
35791                         };
35792                         this.grid.fireEvent('rowclass', this, rowcfg);
35793                         alt.push(rowcfg.rowClass);
35794                     }
35795                     
35796                     rp.alt = alt.join(" ");
35797                     rp.cells = lcb.join("");
35798                     lbuf[lbuf.length] = rt.apply(rp);
35799                     rp.cells = cb.join("");
35800                     buf[buf.length] =  rt.apply(rp);
35801                 }
35802                 return [lbuf.join(""), buf.join("")];
35803             },
35804
35805     renderBody : function(){
35806         var markup = this.renderRows();
35807         var bt = this.templates.body;
35808         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35809     },
35810
35811     /**
35812      * Refreshes the grid
35813      * @param {Boolean} headersToo
35814      */
35815     refresh : function(headersToo){
35816         this.fireEvent("beforerefresh", this);
35817         this.grid.stopEditing();
35818         var result = this.renderBody();
35819         this.lockedBody.update(result[0]);
35820         this.mainBody.update(result[1]);
35821         if(headersToo === true){
35822             this.updateHeaders();
35823             this.updateColumns();
35824             this.updateSplitters();
35825             this.updateHeaderSortState();
35826         }
35827         this.syncRowHeights();
35828         this.layout();
35829         this.fireEvent("refresh", this);
35830     },
35831
35832     handleColumnMove : function(cm, oldIndex, newIndex){
35833         this.indexMap = null;
35834         var s = this.getScrollState();
35835         this.refresh(true);
35836         this.restoreScroll(s);
35837         this.afterMove(newIndex);
35838     },
35839
35840     afterMove : function(colIndex){
35841         if(this.enableMoveAnim && Roo.enableFx){
35842             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35843         }
35844         // if multisort - fix sortOrder, and reload..
35845         if (this.grid.dataSource.multiSort) {
35846             // the we can call sort again..
35847             var dm = this.grid.dataSource;
35848             var cm = this.grid.colModel;
35849             var so = [];
35850             for(var i = 0; i < cm.config.length; i++ ) {
35851                 
35852                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35853                     continue; // dont' bother, it's not in sort list or being set.
35854                 }
35855                 
35856                 so.push(cm.config[i].dataIndex);
35857             };
35858             dm.sortOrder = so;
35859             dm.load(dm.lastOptions);
35860             
35861             
35862         }
35863         
35864     },
35865
35866     updateCell : function(dm, rowIndex, dataIndex){
35867         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35868         if(typeof colIndex == "undefined"){ // not present in grid
35869             return;
35870         }
35871         var cm = this.grid.colModel;
35872         var cell = this.getCell(rowIndex, colIndex);
35873         var cellText = this.getCellText(rowIndex, colIndex);
35874
35875         var p = {
35876             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35877             id : cm.getColumnId(colIndex),
35878             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35879         };
35880         var renderer = cm.getRenderer(colIndex);
35881         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35882         if(typeof val == "undefined" || val === "") {
35883             val = "&#160;";
35884         }
35885         cellText.innerHTML = val;
35886         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35887         this.syncRowHeights(rowIndex, rowIndex);
35888     },
35889
35890     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35891         var maxWidth = 0;
35892         if(this.grid.autoSizeHeaders){
35893             var h = this.getHeaderCellMeasure(colIndex);
35894             maxWidth = Math.max(maxWidth, h.scrollWidth);
35895         }
35896         var tb, index;
35897         if(this.cm.isLocked(colIndex)){
35898             tb = this.getLockedTable();
35899             index = colIndex;
35900         }else{
35901             tb = this.getBodyTable();
35902             index = colIndex - this.cm.getLockedCount();
35903         }
35904         if(tb && tb.rows){
35905             var rows = tb.rows;
35906             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35907             for(var i = 0; i < stopIndex; i++){
35908                 var cell = rows[i].childNodes[index].firstChild;
35909                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35910             }
35911         }
35912         return maxWidth + /*margin for error in IE*/ 5;
35913     },
35914     /**
35915      * Autofit a column to its content.
35916      * @param {Number} colIndex
35917      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35918      */
35919      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35920          if(this.cm.isHidden(colIndex)){
35921              return; // can't calc a hidden column
35922          }
35923         if(forceMinSize){
35924             var cid = this.cm.getColumnId(colIndex);
35925             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35926            if(this.grid.autoSizeHeaders){
35927                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35928            }
35929         }
35930         var newWidth = this.calcColumnWidth(colIndex);
35931         this.cm.setColumnWidth(colIndex,
35932             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35933         if(!suppressEvent){
35934             this.grid.fireEvent("columnresize", colIndex, newWidth);
35935         }
35936     },
35937
35938     /**
35939      * Autofits all columns to their content and then expands to fit any extra space in the grid
35940      */
35941      autoSizeColumns : function(){
35942         var cm = this.grid.colModel;
35943         var colCount = cm.getColumnCount();
35944         for(var i = 0; i < colCount; i++){
35945             this.autoSizeColumn(i, true, true);
35946         }
35947         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35948             this.fitColumns();
35949         }else{
35950             this.updateColumns();
35951             this.layout();
35952         }
35953     },
35954
35955     /**
35956      * Autofits all columns to the grid's width proportionate with their current size
35957      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35958      */
35959     fitColumns : function(reserveScrollSpace){
35960         var cm = this.grid.colModel;
35961         var colCount = cm.getColumnCount();
35962         var cols = [];
35963         var width = 0;
35964         var i, w;
35965         for (i = 0; i < colCount; i++){
35966             if(!cm.isHidden(i) && !cm.isFixed(i)){
35967                 w = cm.getColumnWidth(i);
35968                 cols.push(i);
35969                 cols.push(w);
35970                 width += w;
35971             }
35972         }
35973         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35974         if(reserveScrollSpace){
35975             avail -= 17;
35976         }
35977         var frac = (avail - cm.getTotalWidth())/width;
35978         while (cols.length){
35979             w = cols.pop();
35980             i = cols.pop();
35981             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35982         }
35983         this.updateColumns();
35984         this.layout();
35985     },
35986
35987     onRowSelect : function(rowIndex){
35988         var row = this.getRowComposite(rowIndex);
35989         row.addClass("x-grid-row-selected");
35990     },
35991
35992     onRowDeselect : function(rowIndex){
35993         var row = this.getRowComposite(rowIndex);
35994         row.removeClass("x-grid-row-selected");
35995     },
35996
35997     onCellSelect : function(row, col){
35998         var cell = this.getCell(row, col);
35999         if(cell){
36000             Roo.fly(cell).addClass("x-grid-cell-selected");
36001         }
36002     },
36003
36004     onCellDeselect : function(row, col){
36005         var cell = this.getCell(row, col);
36006         if(cell){
36007             Roo.fly(cell).removeClass("x-grid-cell-selected");
36008         }
36009     },
36010
36011     updateHeaderSortState : function(){
36012         
36013         // sort state can be single { field: xxx, direction : yyy}
36014         // or   { xxx=>ASC , yyy : DESC ..... }
36015         
36016         var mstate = {};
36017         if (!this.ds.multiSort) { 
36018             var state = this.ds.getSortState();
36019             if(!state){
36020                 return;
36021             }
36022             mstate[state.field] = state.direction;
36023             // FIXME... - this is not used here.. but might be elsewhere..
36024             this.sortState = state;
36025             
36026         } else {
36027             mstate = this.ds.sortToggle;
36028         }
36029         //remove existing sort classes..
36030         
36031         var sc = this.sortClasses;
36032         var hds = this.el.select(this.headerSelector).removeClass(sc);
36033         
36034         for(var f in mstate) {
36035         
36036             var sortColumn = this.cm.findColumnIndex(f);
36037             
36038             if(sortColumn != -1){
36039                 var sortDir = mstate[f];        
36040                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36041             }
36042         }
36043         
36044          
36045         
36046     },
36047
36048
36049     handleHeaderClick : function(g, index,e){
36050         
36051         Roo.log("header click");
36052         
36053         if (Roo.isTouch) {
36054             // touch events on header are handled by context
36055             this.handleHdCtx(g,index,e);
36056             return;
36057         }
36058         
36059         
36060         if(this.headersDisabled){
36061             return;
36062         }
36063         var dm = g.dataSource, cm = g.colModel;
36064         if(!cm.isSortable(index)){
36065             return;
36066         }
36067         g.stopEditing();
36068         
36069         if (dm.multiSort) {
36070             // update the sortOrder
36071             var so = [];
36072             for(var i = 0; i < cm.config.length; i++ ) {
36073                 
36074                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36075                     continue; // dont' bother, it's not in sort list or being set.
36076                 }
36077                 
36078                 so.push(cm.config[i].dataIndex);
36079             };
36080             dm.sortOrder = so;
36081         }
36082         
36083         
36084         dm.sort(cm.getDataIndex(index));
36085     },
36086
36087
36088     destroy : function(){
36089         if(this.colMenu){
36090             this.colMenu.removeAll();
36091             Roo.menu.MenuMgr.unregister(this.colMenu);
36092             this.colMenu.getEl().remove();
36093             delete this.colMenu;
36094         }
36095         if(this.hmenu){
36096             this.hmenu.removeAll();
36097             Roo.menu.MenuMgr.unregister(this.hmenu);
36098             this.hmenu.getEl().remove();
36099             delete this.hmenu;
36100         }
36101         if(this.grid.enableColumnMove){
36102             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36103             if(dds){
36104                 for(var dd in dds){
36105                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36106                         var elid = dds[dd].dragElId;
36107                         dds[dd].unreg();
36108                         Roo.get(elid).remove();
36109                     } else if(dds[dd].config.isTarget){
36110                         dds[dd].proxyTop.remove();
36111                         dds[dd].proxyBottom.remove();
36112                         dds[dd].unreg();
36113                     }
36114                     if(Roo.dd.DDM.locationCache[dd]){
36115                         delete Roo.dd.DDM.locationCache[dd];
36116                     }
36117                 }
36118                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36119             }
36120         }
36121         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36122         this.bind(null, null);
36123         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36124     },
36125
36126     handleLockChange : function(){
36127         this.refresh(true);
36128     },
36129
36130     onDenyColumnLock : function(){
36131
36132     },
36133
36134     onDenyColumnHide : function(){
36135
36136     },
36137
36138     handleHdMenuClick : function(item){
36139         var index = this.hdCtxIndex;
36140         var cm = this.cm, ds = this.ds;
36141         switch(item.id){
36142             case "asc":
36143                 ds.sort(cm.getDataIndex(index), "ASC");
36144                 break;
36145             case "desc":
36146                 ds.sort(cm.getDataIndex(index), "DESC");
36147                 break;
36148             case "lock":
36149                 var lc = cm.getLockedCount();
36150                 if(cm.getColumnCount(true) <= lc+1){
36151                     this.onDenyColumnLock();
36152                     return;
36153                 }
36154                 if(lc != index){
36155                     cm.setLocked(index, true, true);
36156                     cm.moveColumn(index, lc);
36157                     this.grid.fireEvent("columnmove", index, lc);
36158                 }else{
36159                     cm.setLocked(index, true);
36160                 }
36161             break;
36162             case "unlock":
36163                 var lc = cm.getLockedCount();
36164                 if((lc-1) != index){
36165                     cm.setLocked(index, false, true);
36166                     cm.moveColumn(index, lc-1);
36167                     this.grid.fireEvent("columnmove", index, lc-1);
36168                 }else{
36169                     cm.setLocked(index, false);
36170                 }
36171             break;
36172             case 'wider': // used to expand cols on touch..
36173             case 'narrow':
36174                 var cw = cm.getColumnWidth(index);
36175                 cw += (item.id == 'wider' ? 1 : -1) * 50;
36176                 cw = Math.max(0, cw);
36177                 cw = Math.min(cw,4000);
36178                 cm.setColumnWidth(index, cw);
36179                 break;
36180                 
36181             default:
36182                 index = cm.getIndexById(item.id.substr(4));
36183                 if(index != -1){
36184                     if(item.checked && cm.getColumnCount(true) <= 1){
36185                         this.onDenyColumnHide();
36186                         return false;
36187                     }
36188                     cm.setHidden(index, item.checked);
36189                 }
36190         }
36191         return true;
36192     },
36193
36194     beforeColMenuShow : function(){
36195         var cm = this.cm,  colCount = cm.getColumnCount();
36196         this.colMenu.removeAll();
36197         for(var i = 0; i < colCount; i++){
36198             this.colMenu.add(new Roo.menu.CheckItem({
36199                 id: "col-"+cm.getColumnId(i),
36200                 text: cm.getColumnHeader(i),
36201                 checked: !cm.isHidden(i),
36202                 hideOnClick:false
36203             }));
36204         }
36205     },
36206
36207     handleHdCtx : function(g, index, e){
36208         e.stopEvent();
36209         var hd = this.getHeaderCell(index);
36210         this.hdCtxIndex = index;
36211         var ms = this.hmenu.items, cm = this.cm;
36212         ms.get("asc").setDisabled(!cm.isSortable(index));
36213         ms.get("desc").setDisabled(!cm.isSortable(index));
36214         if(this.grid.enableColLock !== false){
36215             ms.get("lock").setDisabled(cm.isLocked(index));
36216             ms.get("unlock").setDisabled(!cm.isLocked(index));
36217         }
36218         this.hmenu.show(hd, "tl-bl");
36219     },
36220
36221     handleHdOver : function(e){
36222         var hd = this.findHeaderCell(e.getTarget());
36223         if(hd && !this.headersDisabled){
36224             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36225                this.fly(hd).addClass("x-grid-hd-over");
36226             }
36227         }
36228     },
36229
36230     handleHdOut : function(e){
36231         var hd = this.findHeaderCell(e.getTarget());
36232         if(hd){
36233             this.fly(hd).removeClass("x-grid-hd-over");
36234         }
36235     },
36236
36237     handleSplitDblClick : function(e, t){
36238         var i = this.getCellIndex(t);
36239         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36240             this.autoSizeColumn(i, true);
36241             this.layout();
36242         }
36243     },
36244
36245     render : function(){
36246
36247         var cm = this.cm;
36248         var colCount = cm.getColumnCount();
36249
36250         if(this.grid.monitorWindowResize === true){
36251             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36252         }
36253         var header = this.renderHeaders();
36254         var body = this.templates.body.apply({rows:""});
36255         var html = this.templates.master.apply({
36256             lockedBody: body,
36257             body: body,
36258             lockedHeader: header[0],
36259             header: header[1]
36260         });
36261
36262         //this.updateColumns();
36263
36264         this.grid.getGridEl().dom.innerHTML = html;
36265
36266         this.initElements();
36267         
36268         // a kludge to fix the random scolling effect in webkit
36269         this.el.on("scroll", function() {
36270             this.el.dom.scrollTop=0; // hopefully not recursive..
36271         },this);
36272
36273         this.scroller.on("scroll", this.handleScroll, this);
36274         this.lockedBody.on("mousewheel", this.handleWheel, this);
36275         this.mainBody.on("mousewheel", this.handleWheel, this);
36276
36277         this.mainHd.on("mouseover", this.handleHdOver, this);
36278         this.mainHd.on("mouseout", this.handleHdOut, this);
36279         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36280                 {delegate: "."+this.splitClass});
36281
36282         this.lockedHd.on("mouseover", this.handleHdOver, this);
36283         this.lockedHd.on("mouseout", this.handleHdOut, this);
36284         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36285                 {delegate: "."+this.splitClass});
36286
36287         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36288             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36289         }
36290
36291         this.updateSplitters();
36292
36293         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36294             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36295             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36296         }
36297
36298         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36299             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36300             this.hmenu.add(
36301                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36302                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36303             );
36304             if(this.grid.enableColLock !== false){
36305                 this.hmenu.add('-',
36306                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36307                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36308                 );
36309             }
36310             if (Roo.isTouch) {
36311                  this.hmenu.add('-',
36312                     {id:"wider", text: this.columnsWiderText},
36313                     {id:"narrow", text: this.columnsNarrowText }
36314                 );
36315                 
36316                  
36317             }
36318             
36319             if(this.grid.enableColumnHide !== false){
36320
36321                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36322                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36323                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36324
36325                 this.hmenu.add('-',
36326                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36327                 );
36328             }
36329             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36330
36331             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36332         }
36333
36334         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36335             this.dd = new Roo.grid.GridDragZone(this.grid, {
36336                 ddGroup : this.grid.ddGroup || 'GridDD'
36337             });
36338             
36339         }
36340
36341         /*
36342         for(var i = 0; i < colCount; i++){
36343             if(cm.isHidden(i)){
36344                 this.hideColumn(i);
36345             }
36346             if(cm.config[i].align){
36347                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36348                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36349             }
36350         }*/
36351         
36352         this.updateHeaderSortState();
36353
36354         this.beforeInitialResize();
36355         this.layout(true);
36356
36357         // two part rendering gives faster view to the user
36358         this.renderPhase2.defer(1, this);
36359     },
36360
36361     renderPhase2 : function(){
36362         // render the rows now
36363         this.refresh();
36364         if(this.grid.autoSizeColumns){
36365             this.autoSizeColumns();
36366         }
36367     },
36368
36369     beforeInitialResize : function(){
36370
36371     },
36372
36373     onColumnSplitterMoved : function(i, w){
36374         this.userResized = true;
36375         var cm = this.grid.colModel;
36376         cm.setColumnWidth(i, w, true);
36377         var cid = cm.getColumnId(i);
36378         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36379         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36380         this.updateSplitters();
36381         this.layout();
36382         this.grid.fireEvent("columnresize", i, w);
36383     },
36384
36385     syncRowHeights : function(startIndex, endIndex){
36386         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36387             startIndex = startIndex || 0;
36388             var mrows = this.getBodyTable().rows;
36389             var lrows = this.getLockedTable().rows;
36390             var len = mrows.length-1;
36391             endIndex = Math.min(endIndex || len, len);
36392             for(var i = startIndex; i <= endIndex; i++){
36393                 var m = mrows[i], l = lrows[i];
36394                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36395                 m.style.height = l.style.height = h + "px";
36396             }
36397         }
36398     },
36399
36400     layout : function(initialRender, is2ndPass)
36401     {
36402         var g = this.grid;
36403         var auto = g.autoHeight;
36404         var scrollOffset = 16;
36405         var c = g.getGridEl(), cm = this.cm,
36406                 expandCol = g.autoExpandColumn,
36407                 gv = this;
36408         //c.beginMeasure();
36409
36410         if(!c.dom.offsetWidth){ // display:none?
36411             if(initialRender){
36412                 this.lockedWrap.show();
36413                 this.mainWrap.show();
36414             }
36415             return;
36416         }
36417
36418         var hasLock = this.cm.isLocked(0);
36419
36420         var tbh = this.headerPanel.getHeight();
36421         var bbh = this.footerPanel.getHeight();
36422
36423         if(auto){
36424             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36425             var newHeight = ch + c.getBorderWidth("tb");
36426             if(g.maxHeight){
36427                 newHeight = Math.min(g.maxHeight, newHeight);
36428             }
36429             c.setHeight(newHeight);
36430         }
36431
36432         if(g.autoWidth){
36433             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36434         }
36435
36436         var s = this.scroller;
36437
36438         var csize = c.getSize(true);
36439
36440         this.el.setSize(csize.width, csize.height);
36441
36442         this.headerPanel.setWidth(csize.width);
36443         this.footerPanel.setWidth(csize.width);
36444
36445         var hdHeight = this.mainHd.getHeight();
36446         var vw = csize.width;
36447         var vh = csize.height - (tbh + bbh);
36448
36449         s.setSize(vw, vh);
36450
36451         var bt = this.getBodyTable();
36452         
36453         if(cm.getLockedCount() == cm.config.length){
36454             bt = this.getLockedTable();
36455         }
36456         
36457         var ltWidth = hasLock ?
36458                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36459
36460         var scrollHeight = bt.offsetHeight;
36461         var scrollWidth = ltWidth + bt.offsetWidth;
36462         var vscroll = false, hscroll = false;
36463
36464         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36465
36466         var lw = this.lockedWrap, mw = this.mainWrap;
36467         var lb = this.lockedBody, mb = this.mainBody;
36468
36469         setTimeout(function(){
36470             var t = s.dom.offsetTop;
36471             var w = s.dom.clientWidth,
36472                 h = s.dom.clientHeight;
36473
36474             lw.setTop(t);
36475             lw.setSize(ltWidth, h);
36476
36477             mw.setLeftTop(ltWidth, t);
36478             mw.setSize(w-ltWidth, h);
36479
36480             lb.setHeight(h-hdHeight);
36481             mb.setHeight(h-hdHeight);
36482
36483             if(is2ndPass !== true && !gv.userResized && expandCol){
36484                 // high speed resize without full column calculation
36485                 
36486                 var ci = cm.getIndexById(expandCol);
36487                 if (ci < 0) {
36488                     ci = cm.findColumnIndex(expandCol);
36489                 }
36490                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36491                 var expandId = cm.getColumnId(ci);
36492                 var  tw = cm.getTotalWidth(false);
36493                 var currentWidth = cm.getColumnWidth(ci);
36494                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36495                 if(currentWidth != cw){
36496                     cm.setColumnWidth(ci, cw, true);
36497                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36498                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36499                     gv.updateSplitters();
36500                     gv.layout(false, true);
36501                 }
36502             }
36503
36504             if(initialRender){
36505                 lw.show();
36506                 mw.show();
36507             }
36508             //c.endMeasure();
36509         }, 10);
36510     },
36511
36512     onWindowResize : function(){
36513         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36514             return;
36515         }
36516         this.layout();
36517     },
36518
36519     appendFooter : function(parentEl){
36520         return null;
36521     },
36522
36523     sortAscText : "Sort Ascending",
36524     sortDescText : "Sort Descending",
36525     lockText : "Lock Column",
36526     unlockText : "Unlock Column",
36527     columnsText : "Columns",
36528  
36529     columnsWiderText : "Wider",
36530     columnsNarrowText : "Thinner"
36531 });
36532
36533
36534 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36535     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36536     this.proxy.el.addClass('x-grid3-col-dd');
36537 };
36538
36539 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36540     handleMouseDown : function(e){
36541
36542     },
36543
36544     callHandleMouseDown : function(e){
36545         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36546     }
36547 });
36548 /*
36549  * Based on:
36550  * Ext JS Library 1.1.1
36551  * Copyright(c) 2006-2007, Ext JS, LLC.
36552  *
36553  * Originally Released Under LGPL - original licence link has changed is not relivant.
36554  *
36555  * Fork - LGPL
36556  * <script type="text/javascript">
36557  */
36558  /**
36559  * @extends Roo.dd.DDProxy
36560  * @class Roo.grid.SplitDragZone
36561  * Support for Column Header resizing
36562  * @constructor
36563  * @param {Object} config
36564  */
36565 // private
36566 // This is a support class used internally by the Grid components
36567 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36568     this.grid = grid;
36569     this.view = grid.getView();
36570     this.proxy = this.view.resizeProxy;
36571     Roo.grid.SplitDragZone.superclass.constructor.call(
36572         this,
36573         hd, // ID
36574         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
36575         {  // CONFIG
36576             dragElId : Roo.id(this.proxy.dom),
36577             resizeFrame:false
36578         }
36579     );
36580     
36581     this.setHandleElId(Roo.id(hd));
36582     if (hd2 !== false) {
36583         this.setOuterHandleElId(Roo.id(hd2));
36584     }
36585     
36586     this.scroll = false;
36587 };
36588 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36589     fly: Roo.Element.fly,
36590
36591     b4StartDrag : function(x, y){
36592         this.view.headersDisabled = true;
36593         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
36594                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
36595         );
36596         this.proxy.setHeight(h);
36597         
36598         // for old system colWidth really stored the actual width?
36599         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
36600         // which in reality did not work.. - it worked only for fixed sizes
36601         // for resizable we need to use actual sizes.
36602         var w = this.cm.getColumnWidth(this.cellIndex);
36603         if (!this.view.mainWrap) {
36604             // bootstrap.
36605             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
36606         }
36607         
36608         
36609         
36610         // this was w-this.grid.minColumnWidth;
36611         // doesnt really make sense? - w = thie curren width or the rendered one?
36612         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36613         this.resetConstraints();
36614         this.setXConstraint(minw, 1000);
36615         this.setYConstraint(0, 0);
36616         this.minX = x - minw;
36617         this.maxX = x + 1000;
36618         this.startPos = x;
36619         if (!this.view.mainWrap) { // this is Bootstrap code..
36620             this.getDragEl().style.display='block';
36621         }
36622         
36623         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36624     },
36625
36626
36627     handleMouseDown : function(e){
36628         ev = Roo.EventObject.setEvent(e);
36629         var t = this.fly(ev.getTarget());
36630         if(t.hasClass("x-grid-split")){
36631             this.cellIndex = this.view.getCellIndex(t.dom);
36632             this.split = t.dom;
36633             this.cm = this.grid.colModel;
36634             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36635                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36636             }
36637         }
36638     },
36639
36640     endDrag : function(e){
36641         this.view.headersDisabled = false;
36642         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36643         var diff = endX - this.startPos;
36644         // 
36645         var w = this.cm.getColumnWidth(this.cellIndex);
36646         if (!this.view.mainWrap) {
36647             w = 0;
36648         }
36649         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
36650     },
36651
36652     autoOffset : function(){
36653         this.setDelta(0,0);
36654     }
36655 });/*
36656  * Based on:
36657  * Ext JS Library 1.1.1
36658  * Copyright(c) 2006-2007, Ext JS, LLC.
36659  *
36660  * Originally Released Under LGPL - original licence link has changed is not relivant.
36661  *
36662  * Fork - LGPL
36663  * <script type="text/javascript">
36664  */
36665  
36666 // private
36667 // This is a support class used internally by the Grid components
36668 Roo.grid.GridDragZone = function(grid, config){
36669     this.view = grid.getView();
36670     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36671     if(this.view.lockedBody){
36672         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36673         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36674     }
36675     this.scroll = false;
36676     this.grid = grid;
36677     this.ddel = document.createElement('div');
36678     this.ddel.className = 'x-grid-dd-wrap';
36679 };
36680
36681 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36682     ddGroup : "GridDD",
36683
36684     getDragData : function(e){
36685         var t = Roo.lib.Event.getTarget(e);
36686         var rowIndex = this.view.findRowIndex(t);
36687         var sm = this.grid.selModel;
36688             
36689         //Roo.log(rowIndex);
36690         
36691         if (sm.getSelectedCell) {
36692             // cell selection..
36693             if (!sm.getSelectedCell()) {
36694                 return false;
36695             }
36696             if (rowIndex != sm.getSelectedCell()[0]) {
36697                 return false;
36698             }
36699         
36700         }
36701         if (sm.getSelections && sm.getSelections().length < 1) {
36702             return false;
36703         }
36704         
36705         
36706         // before it used to all dragging of unseleted... - now we dont do that.
36707         if(rowIndex !== false){
36708             
36709             // if editorgrid.. 
36710             
36711             
36712             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
36713                
36714             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36715               //  
36716             //}
36717             if (e.hasModifier()){
36718                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36719             }
36720             
36721             Roo.log("getDragData");
36722             
36723             return {
36724                 grid: this.grid,
36725                 ddel: this.ddel,
36726                 rowIndex: rowIndex,
36727                 selections: sm.getSelections ? sm.getSelections() : (
36728                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
36729             };
36730         }
36731         return false;
36732     },
36733     
36734     
36735     onInitDrag : function(e){
36736         var data = this.dragData;
36737         this.ddel.innerHTML = this.grid.getDragDropText();
36738         this.proxy.update(this.ddel);
36739         // fire start drag?
36740     },
36741
36742     afterRepair : function(){
36743         this.dragging = false;
36744     },
36745
36746     getRepairXY : function(e, data){
36747         return false;
36748     },
36749
36750     onEndDrag : function(data, e){
36751         // fire end drag?
36752     },
36753
36754     onValidDrop : function(dd, e, id){
36755         // fire drag drop?
36756         this.hideProxy();
36757     },
36758
36759     beforeInvalidDrop : function(e, id){
36760
36761     }
36762 });/*
36763  * Based on:
36764  * Ext JS Library 1.1.1
36765  * Copyright(c) 2006-2007, Ext JS, LLC.
36766  *
36767  * Originally Released Under LGPL - original licence link has changed is not relivant.
36768  *
36769  * Fork - LGPL
36770  * <script type="text/javascript">
36771  */
36772  
36773
36774 /**
36775  * @class Roo.grid.ColumnModel
36776  * @extends Roo.util.Observable
36777  * This is the default implementation of a ColumnModel used by the Grid. It defines
36778  * the columns in the grid.
36779  * <br>Usage:<br>
36780  <pre><code>
36781  var colModel = new Roo.grid.ColumnModel([
36782         {header: "Ticker", width: 60, sortable: true, locked: true},
36783         {header: "Company Name", width: 150, sortable: true},
36784         {header: "Market Cap.", width: 100, sortable: true},
36785         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36786         {header: "Employees", width: 100, sortable: true, resizable: false}
36787  ]);
36788  </code></pre>
36789  * <p>
36790  
36791  * The config options listed for this class are options which may appear in each
36792  * individual column definition.
36793  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36794  * @constructor
36795  * @param {Object} config An Array of column config objects. See this class's
36796  * config objects for details.
36797 */
36798 Roo.grid.ColumnModel = function(config){
36799         /**
36800      * The config passed into the constructor
36801      */
36802     this.config = []; //config;
36803     this.lookup = {};
36804
36805     // if no id, create one
36806     // if the column does not have a dataIndex mapping,
36807     // map it to the order it is in the config
36808     for(var i = 0, len = config.length; i < len; i++){
36809         this.addColumn(config[i]);
36810         
36811     }
36812
36813     /**
36814      * The width of columns which have no width specified (defaults to 100)
36815      * @type Number
36816      */
36817     this.defaultWidth = 100;
36818
36819     /**
36820      * Default sortable of columns which have no sortable specified (defaults to false)
36821      * @type Boolean
36822      */
36823     this.defaultSortable = false;
36824
36825     this.addEvents({
36826         /**
36827              * @event widthchange
36828              * Fires when the width of a column changes.
36829              * @param {ColumnModel} this
36830              * @param {Number} columnIndex The column index
36831              * @param {Number} newWidth The new width
36832              */
36833             "widthchange": true,
36834         /**
36835              * @event headerchange
36836              * Fires when the text of a header changes.
36837              * @param {ColumnModel} this
36838              * @param {Number} columnIndex The column index
36839              * @param {Number} newText The new header text
36840              */
36841             "headerchange": true,
36842         /**
36843              * @event hiddenchange
36844              * Fires when a column is hidden or "unhidden".
36845              * @param {ColumnModel} this
36846              * @param {Number} columnIndex The column index
36847              * @param {Boolean} hidden true if hidden, false otherwise
36848              */
36849             "hiddenchange": true,
36850             /**
36851          * @event columnmoved
36852          * Fires when a column is moved.
36853          * @param {ColumnModel} this
36854          * @param {Number} oldIndex
36855          * @param {Number} newIndex
36856          */
36857         "columnmoved" : true,
36858         /**
36859          * @event columlockchange
36860          * Fires when a column's locked state is changed
36861          * @param {ColumnModel} this
36862          * @param {Number} colIndex
36863          * @param {Boolean} locked true if locked
36864          */
36865         "columnlockchange" : true
36866     });
36867     Roo.grid.ColumnModel.superclass.constructor.call(this);
36868 };
36869 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36870     /**
36871      * @cfg {String} header The header text to display in the Grid view.
36872      */
36873         /**
36874      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
36875      */
36876         /**
36877      * @cfg {String} smHeader Header at Bootsrap Small width
36878      */
36879         /**
36880      * @cfg {String} mdHeader Header at Bootsrap Medium width
36881      */
36882         /**
36883      * @cfg {String} lgHeader Header at Bootsrap Large width
36884      */
36885         /**
36886      * @cfg {String} xlHeader Header at Bootsrap extra Large width
36887      */
36888     /**
36889      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36890      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36891      * specified, the column's index is used as an index into the Record's data Array.
36892      */
36893     /**
36894      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36895      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36896      */
36897     /**
36898      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36899      * Defaults to the value of the {@link #defaultSortable} property.
36900      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36901      */
36902     /**
36903      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36904      */
36905     /**
36906      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36907      */
36908     /**
36909      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36910      */
36911     /**
36912      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36913      */
36914     /**
36915      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36916      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36917      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
36918      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
36919      */
36920        /**
36921      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36922      */
36923     /**
36924      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36925      */
36926     /**
36927      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
36928      */
36929     /**
36930      * @cfg {String} cursor (Optional)
36931      */
36932     /**
36933      * @cfg {String} tooltip (Optional)
36934      */
36935     /**
36936      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
36937      */
36938     /**
36939      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
36940      */
36941     /**
36942      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
36943      */
36944     /**
36945      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
36946      */
36947         /**
36948      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
36949      */
36950     /**
36951      * Returns the id of the column at the specified index.
36952      * @param {Number} index The column index
36953      * @return {String} the id
36954      */
36955     getColumnId : function(index){
36956         return this.config[index].id;
36957     },
36958
36959     /**
36960      * Returns the column for a specified id.
36961      * @param {String} id The column id
36962      * @return {Object} the column
36963      */
36964     getColumnById : function(id){
36965         return this.lookup[id];
36966     },
36967
36968     
36969     /**
36970      * Returns the column Object for a specified dataIndex.
36971      * @param {String} dataIndex The column dataIndex
36972      * @return {Object|Boolean} the column or false if not found
36973      */
36974     getColumnByDataIndex: function(dataIndex){
36975         var index = this.findColumnIndex(dataIndex);
36976         return index > -1 ? this.config[index] : false;
36977     },
36978     
36979     /**
36980      * Returns the index for a specified column id.
36981      * @param {String} id The column id
36982      * @return {Number} the index, or -1 if not found
36983      */
36984     getIndexById : function(id){
36985         for(var i = 0, len = this.config.length; i < len; i++){
36986             if(this.config[i].id == id){
36987                 return i;
36988             }
36989         }
36990         return -1;
36991     },
36992     
36993     /**
36994      * Returns the index for a specified column dataIndex.
36995      * @param {String} dataIndex The column dataIndex
36996      * @return {Number} the index, or -1 if not found
36997      */
36998     
36999     findColumnIndex : function(dataIndex){
37000         for(var i = 0, len = this.config.length; i < len; i++){
37001             if(this.config[i].dataIndex == dataIndex){
37002                 return i;
37003             }
37004         }
37005         return -1;
37006     },
37007     
37008     
37009     moveColumn : function(oldIndex, newIndex){
37010         var c = this.config[oldIndex];
37011         this.config.splice(oldIndex, 1);
37012         this.config.splice(newIndex, 0, c);
37013         this.dataMap = null;
37014         this.fireEvent("columnmoved", this, oldIndex, newIndex);
37015     },
37016
37017     isLocked : function(colIndex){
37018         return this.config[colIndex].locked === true;
37019     },
37020
37021     setLocked : function(colIndex, value, suppressEvent){
37022         if(this.isLocked(colIndex) == value){
37023             return;
37024         }
37025         this.config[colIndex].locked = value;
37026         if(!suppressEvent){
37027             this.fireEvent("columnlockchange", this, colIndex, value);
37028         }
37029     },
37030
37031     getTotalLockedWidth : function(){
37032         var totalWidth = 0;
37033         for(var i = 0; i < this.config.length; i++){
37034             if(this.isLocked(i) && !this.isHidden(i)){
37035                 this.totalWidth += this.getColumnWidth(i);
37036             }
37037         }
37038         return totalWidth;
37039     },
37040
37041     getLockedCount : function(){
37042         for(var i = 0, len = this.config.length; i < len; i++){
37043             if(!this.isLocked(i)){
37044                 return i;
37045             }
37046         }
37047         
37048         return this.config.length;
37049     },
37050
37051     /**
37052      * Returns the number of columns.
37053      * @return {Number}
37054      */
37055     getColumnCount : function(visibleOnly){
37056         if(visibleOnly === true){
37057             var c = 0;
37058             for(var i = 0, len = this.config.length; i < len; i++){
37059                 if(!this.isHidden(i)){
37060                     c++;
37061                 }
37062             }
37063             return c;
37064         }
37065         return this.config.length;
37066     },
37067
37068     /**
37069      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37070      * @param {Function} fn
37071      * @param {Object} scope (optional)
37072      * @return {Array} result
37073      */
37074     getColumnsBy : function(fn, scope){
37075         var r = [];
37076         for(var i = 0, len = this.config.length; i < len; i++){
37077             var c = this.config[i];
37078             if(fn.call(scope||this, c, i) === true){
37079                 r[r.length] = c;
37080             }
37081         }
37082         return r;
37083     },
37084
37085     /**
37086      * Returns true if the specified column is sortable.
37087      * @param {Number} col The column index
37088      * @return {Boolean}
37089      */
37090     isSortable : function(col){
37091         if(typeof this.config[col].sortable == "undefined"){
37092             return this.defaultSortable;
37093         }
37094         return this.config[col].sortable;
37095     },
37096
37097     /**
37098      * Returns the rendering (formatting) function defined for the column.
37099      * @param {Number} col The column index.
37100      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37101      */
37102     getRenderer : function(col){
37103         if(!this.config[col].renderer){
37104             return Roo.grid.ColumnModel.defaultRenderer;
37105         }
37106         return this.config[col].renderer;
37107     },
37108
37109     /**
37110      * Sets the rendering (formatting) function for a column.
37111      * @param {Number} col The column index
37112      * @param {Function} fn The function to use to process the cell's raw data
37113      * to return HTML markup for the grid view. The render function is called with
37114      * the following parameters:<ul>
37115      * <li>Data value.</li>
37116      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37117      * <li>css A CSS style string to apply to the table cell.</li>
37118      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37119      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37120      * <li>Row index</li>
37121      * <li>Column index</li>
37122      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37123      */
37124     setRenderer : function(col, fn){
37125         this.config[col].renderer = fn;
37126     },
37127
37128     /**
37129      * Returns the width for the specified column.
37130      * @param {Number} col The column index
37131      * @param (optional) {String} gridSize bootstrap width size.
37132      * @return {Number}
37133      */
37134     getColumnWidth : function(col, gridSize)
37135         {
37136                 var cfg = this.config[col];
37137                 
37138                 if (typeof(gridSize) == 'undefined') {
37139                         return cfg.width * 1 || this.defaultWidth;
37140                 }
37141                 if (gridSize === false) { // if we set it..
37142                         return cfg.width || false;
37143                 }
37144                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
37145                 
37146                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
37147                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
37148                                 continue;
37149                         }
37150                         return cfg[ sizes[i] ];
37151                 }
37152                 return 1;
37153                 
37154     },
37155
37156     /**
37157      * Sets the width for a column.
37158      * @param {Number} col The column index
37159      * @param {Number} width The new width
37160      */
37161     setColumnWidth : function(col, width, suppressEvent){
37162         this.config[col].width = width;
37163         this.totalWidth = null;
37164         if(!suppressEvent){
37165              this.fireEvent("widthchange", this, col, width);
37166         }
37167     },
37168
37169     /**
37170      * Returns the total width of all columns.
37171      * @param {Boolean} includeHidden True to include hidden column widths
37172      * @return {Number}
37173      */
37174     getTotalWidth : function(includeHidden){
37175         if(!this.totalWidth){
37176             this.totalWidth = 0;
37177             for(var i = 0, len = this.config.length; i < len; i++){
37178                 if(includeHidden || !this.isHidden(i)){
37179                     this.totalWidth += this.getColumnWidth(i);
37180                 }
37181             }
37182         }
37183         return this.totalWidth;
37184     },
37185
37186     /**
37187      * Returns the header for the specified column.
37188      * @param {Number} col The column index
37189      * @return {String}
37190      */
37191     getColumnHeader : function(col){
37192         return this.config[col].header;
37193     },
37194
37195     /**
37196      * Sets the header for a column.
37197      * @param {Number} col The column index
37198      * @param {String} header The new header
37199      */
37200     setColumnHeader : function(col, header){
37201         this.config[col].header = header;
37202         this.fireEvent("headerchange", this, col, header);
37203     },
37204
37205     /**
37206      * Returns the tooltip for the specified column.
37207      * @param {Number} col The column index
37208      * @return {String}
37209      */
37210     getColumnTooltip : function(col){
37211             return this.config[col].tooltip;
37212     },
37213     /**
37214      * Sets the tooltip for a column.
37215      * @param {Number} col The column index
37216      * @param {String} tooltip The new tooltip
37217      */
37218     setColumnTooltip : function(col, tooltip){
37219             this.config[col].tooltip = tooltip;
37220     },
37221
37222     /**
37223      * Returns the dataIndex for the specified column.
37224      * @param {Number} col The column index
37225      * @return {Number}
37226      */
37227     getDataIndex : function(col){
37228         return this.config[col].dataIndex;
37229     },
37230
37231     /**
37232      * Sets the dataIndex for a column.
37233      * @param {Number} col The column index
37234      * @param {Number} dataIndex The new dataIndex
37235      */
37236     setDataIndex : function(col, dataIndex){
37237         this.config[col].dataIndex = dataIndex;
37238     },
37239
37240     
37241     
37242     /**
37243      * Returns true if the cell is editable.
37244      * @param {Number} colIndex The column index
37245      * @param {Number} rowIndex The row index - this is nto actually used..?
37246      * @return {Boolean}
37247      */
37248     isCellEditable : function(colIndex, rowIndex){
37249         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
37250     },
37251
37252     /**
37253      * Returns the editor defined for the cell/column.
37254      * return false or null to disable editing.
37255      * @param {Number} colIndex The column index
37256      * @param {Number} rowIndex The row index
37257      * @return {Object}
37258      */
37259     getCellEditor : function(colIndex, rowIndex){
37260         return this.config[colIndex].editor;
37261     },
37262
37263     /**
37264      * Sets if a column is editable.
37265      * @param {Number} col The column index
37266      * @param {Boolean} editable True if the column is editable
37267      */
37268     setEditable : function(col, editable){
37269         this.config[col].editable = editable;
37270     },
37271
37272
37273     /**
37274      * Returns true if the column is hidden.
37275      * @param {Number} colIndex The column index
37276      * @return {Boolean}
37277      */
37278     isHidden : function(colIndex){
37279         return this.config[colIndex].hidden;
37280     },
37281
37282
37283     /**
37284      * Returns true if the column width cannot be changed
37285      */
37286     isFixed : function(colIndex){
37287         return this.config[colIndex].fixed;
37288     },
37289
37290     /**
37291      * Returns true if the column can be resized
37292      * @return {Boolean}
37293      */
37294     isResizable : function(colIndex){
37295         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
37296     },
37297     /**
37298      * Sets if a column is hidden.
37299      * @param {Number} colIndex The column index
37300      * @param {Boolean} hidden True if the column is hidden
37301      */
37302     setHidden : function(colIndex, hidden){
37303         this.config[colIndex].hidden = hidden;
37304         this.totalWidth = null;
37305         this.fireEvent("hiddenchange", this, colIndex, hidden);
37306     },
37307
37308     /**
37309      * Sets the editor for a column.
37310      * @param {Number} col The column index
37311      * @param {Object} editor The editor object
37312      */
37313     setEditor : function(col, editor){
37314         this.config[col].editor = editor;
37315     },
37316     /**
37317      * Add a column (experimental...) - defaults to adding to the end..
37318      * @param {Object} config 
37319     */
37320     addColumn : function(c)
37321     {
37322     
37323         var i = this.config.length;
37324         this.config[i] = c;
37325         
37326         if(typeof c.dataIndex == "undefined"){
37327             c.dataIndex = i;
37328         }
37329         if(typeof c.renderer == "string"){
37330             c.renderer = Roo.util.Format[c.renderer];
37331         }
37332         if(typeof c.id == "undefined"){
37333             c.id = Roo.id();
37334         }
37335         if(c.editor && c.editor.xtype){
37336             c.editor  = Roo.factory(c.editor, Roo.grid);
37337         }
37338         if(c.editor && c.editor.isFormField){
37339             c.editor = new Roo.grid.GridEditor(c.editor);
37340         }
37341         this.lookup[c.id] = c;
37342     }
37343     
37344 });
37345
37346 Roo.grid.ColumnModel.defaultRenderer = function(value)
37347 {
37348     if(typeof value == "object") {
37349         return value;
37350     }
37351         if(typeof value == "string" && value.length < 1){
37352             return "&#160;";
37353         }
37354     
37355         return String.format("{0}", value);
37356 };
37357
37358 // Alias for backwards compatibility
37359 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
37360 /*
37361  * Based on:
37362  * Ext JS Library 1.1.1
37363  * Copyright(c) 2006-2007, Ext JS, LLC.
37364  *
37365  * Originally Released Under LGPL - original licence link has changed is not relivant.
37366  *
37367  * Fork - LGPL
37368  * <script type="text/javascript">
37369  */
37370
37371 /**
37372  * @class Roo.grid.AbstractSelectionModel
37373  * @extends Roo.util.Observable
37374  * @abstract
37375  * Abstract base class for grid SelectionModels.  It provides the interface that should be
37376  * implemented by descendant classes.  This class should not be directly instantiated.
37377  * @constructor
37378  */
37379 Roo.grid.AbstractSelectionModel = function(){
37380     this.locked = false;
37381     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
37382 };
37383
37384 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
37385     /** @ignore Called by the grid automatically. Do not call directly. */
37386     init : function(grid){
37387         this.grid = grid;
37388         this.initEvents();
37389     },
37390
37391     /**
37392      * Locks the selections.
37393      */
37394     lock : function(){
37395         this.locked = true;
37396     },
37397
37398     /**
37399      * Unlocks the selections.
37400      */
37401     unlock : function(){
37402         this.locked = false;
37403     },
37404
37405     /**
37406      * Returns true if the selections are locked.
37407      * @return {Boolean}
37408      */
37409     isLocked : function(){
37410         return this.locked;
37411     }
37412 });/*
37413  * Based on:
37414  * Ext JS Library 1.1.1
37415  * Copyright(c) 2006-2007, Ext JS, LLC.
37416  *
37417  * Originally Released Under LGPL - original licence link has changed is not relivant.
37418  *
37419  * Fork - LGPL
37420  * <script type="text/javascript">
37421  */
37422 /**
37423  * @extends Roo.grid.AbstractSelectionModel
37424  * @class Roo.grid.RowSelectionModel
37425  * The default SelectionModel used by {@link Roo.grid.Grid}.
37426  * It supports multiple selections and keyboard selection/navigation. 
37427  * @constructor
37428  * @param {Object} config
37429  */
37430 Roo.grid.RowSelectionModel = function(config){
37431     Roo.apply(this, config);
37432     this.selections = new Roo.util.MixedCollection(false, function(o){
37433         return o.id;
37434     });
37435
37436     this.last = false;
37437     this.lastActive = false;
37438
37439     this.addEvents({
37440         /**
37441         * @event selectionchange
37442         * Fires when the selection changes
37443         * @param {SelectionModel} this
37444         */
37445        "selectionchange" : true,
37446        /**
37447         * @event afterselectionchange
37448         * Fires after the selection changes (eg. by key press or clicking)
37449         * @param {SelectionModel} this
37450         */
37451        "afterselectionchange" : true,
37452        /**
37453         * @event beforerowselect
37454         * Fires when a row is selected being selected, return false to cancel.
37455         * @param {SelectionModel} this
37456         * @param {Number} rowIndex The selected index
37457         * @param {Boolean} keepExisting False if other selections will be cleared
37458         */
37459        "beforerowselect" : true,
37460        /**
37461         * @event rowselect
37462         * Fires when a row is selected.
37463         * @param {SelectionModel} this
37464         * @param {Number} rowIndex The selected index
37465         * @param {Roo.data.Record} r The record
37466         */
37467        "rowselect" : true,
37468        /**
37469         * @event rowdeselect
37470         * Fires when a row is deselected.
37471         * @param {SelectionModel} this
37472         * @param {Number} rowIndex The selected index
37473         */
37474         "rowdeselect" : true
37475     });
37476     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37477     this.locked = false;
37478 };
37479
37480 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
37481     /**
37482      * @cfg {Boolean} singleSelect
37483      * True to allow selection of only one row at a time (defaults to false)
37484      */
37485     singleSelect : false,
37486
37487     // private
37488     initEvents : function(){
37489
37490         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37491             this.grid.on("mousedown", this.handleMouseDown, this);
37492         }else{ // allow click to work like normal
37493             this.grid.on("rowclick", this.handleDragableRowClick, this);
37494         }
37495         // bootstrap does not have a view..
37496         var view = this.grid.view ? this.grid.view : this.grid;
37497         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37498             "up" : function(e){
37499                 if(!e.shiftKey){
37500                     this.selectPrevious(e.shiftKey);
37501                 }else if(this.last !== false && this.lastActive !== false){
37502                     var last = this.last;
37503                     this.selectRange(this.last,  this.lastActive-1);
37504                     view.focusRow(this.lastActive);
37505                     if(last !== false){
37506                         this.last = last;
37507                     }
37508                 }else{
37509                     this.selectFirstRow();
37510                 }
37511                 this.fireEvent("afterselectionchange", this);
37512             },
37513             "down" : function(e){
37514                 if(!e.shiftKey){
37515                     this.selectNext(e.shiftKey);
37516                 }else if(this.last !== false && this.lastActive !== false){
37517                     var last = this.last;
37518                     this.selectRange(this.last,  this.lastActive+1);
37519                     view.focusRow(this.lastActive);
37520                     if(last !== false){
37521                         this.last = last;
37522                     }
37523                 }else{
37524                     this.selectFirstRow();
37525                 }
37526                 this.fireEvent("afterselectionchange", this);
37527             },
37528             scope: this
37529         });
37530
37531          
37532         view.on("refresh", this.onRefresh, this);
37533         view.on("rowupdated", this.onRowUpdated, this);
37534         view.on("rowremoved", this.onRemove, this);
37535     },
37536
37537     // private
37538     onRefresh : function(){
37539         var ds = this.grid.ds, i, v = this.grid.view;
37540         var s = this.selections;
37541         s.each(function(r){
37542             if((i = ds.indexOfId(r.id)) != -1){
37543                 v.onRowSelect(i);
37544                 s.add(ds.getAt(i)); // updating the selection relate data
37545             }else{
37546                 s.remove(r);
37547             }
37548         });
37549     },
37550
37551     // private
37552     onRemove : function(v, index, r){
37553         this.selections.remove(r);
37554     },
37555
37556     // private
37557     onRowUpdated : function(v, index, r){
37558         if(this.isSelected(r)){
37559             v.onRowSelect(index);
37560         }
37561     },
37562
37563     /**
37564      * Select records.
37565      * @param {Array} records The records to select
37566      * @param {Boolean} keepExisting (optional) True to keep existing selections
37567      */
37568     selectRecords : function(records, keepExisting){
37569         if(!keepExisting){
37570             this.clearSelections();
37571         }
37572         var ds = this.grid.ds;
37573         for(var i = 0, len = records.length; i < len; i++){
37574             this.selectRow(ds.indexOf(records[i]), true);
37575         }
37576     },
37577
37578     /**
37579      * Gets the number of selected rows.
37580      * @return {Number}
37581      */
37582     getCount : function(){
37583         return this.selections.length;
37584     },
37585
37586     /**
37587      * Selects the first row in the grid.
37588      */
37589     selectFirstRow : function(){
37590         this.selectRow(0);
37591     },
37592
37593     /**
37594      * Select the last row.
37595      * @param {Boolean} keepExisting (optional) True to keep existing selections
37596      */
37597     selectLastRow : function(keepExisting){
37598         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
37599     },
37600
37601     /**
37602      * Selects the row immediately following the last selected row.
37603      * @param {Boolean} keepExisting (optional) True to keep existing selections
37604      */
37605     selectNext : function(keepExisting){
37606         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
37607             this.selectRow(this.last+1, keepExisting);
37608             var view = this.grid.view ? this.grid.view : this.grid;
37609             view.focusRow(this.last);
37610         }
37611     },
37612
37613     /**
37614      * Selects the row that precedes the last selected row.
37615      * @param {Boolean} keepExisting (optional) True to keep existing selections
37616      */
37617     selectPrevious : function(keepExisting){
37618         if(this.last){
37619             this.selectRow(this.last-1, keepExisting);
37620             var view = this.grid.view ? this.grid.view : this.grid;
37621             view.focusRow(this.last);
37622         }
37623     },
37624
37625     /**
37626      * Returns the selected records
37627      * @return {Array} Array of selected records
37628      */
37629     getSelections : function(){
37630         return [].concat(this.selections.items);
37631     },
37632
37633     /**
37634      * Returns the first selected record.
37635      * @return {Record}
37636      */
37637     getSelected : function(){
37638         return this.selections.itemAt(0);
37639     },
37640
37641
37642     /**
37643      * Clears all selections.
37644      */
37645     clearSelections : function(fast){
37646         if(this.locked) {
37647             return;
37648         }
37649         if(fast !== true){
37650             var ds = this.grid.ds;
37651             var s = this.selections;
37652             s.each(function(r){
37653                 this.deselectRow(ds.indexOfId(r.id));
37654             }, this);
37655             s.clear();
37656         }else{
37657             this.selections.clear();
37658         }
37659         this.last = false;
37660     },
37661
37662
37663     /**
37664      * Selects all rows.
37665      */
37666     selectAll : function(){
37667         if(this.locked) {
37668             return;
37669         }
37670         this.selections.clear();
37671         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
37672             this.selectRow(i, true);
37673         }
37674     },
37675
37676     /**
37677      * Returns True if there is a selection.
37678      * @return {Boolean}
37679      */
37680     hasSelection : function(){
37681         return this.selections.length > 0;
37682     },
37683
37684     /**
37685      * Returns True if the specified row is selected.
37686      * @param {Number/Record} record The record or index of the record to check
37687      * @return {Boolean}
37688      */
37689     isSelected : function(index){
37690         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
37691         return (r && this.selections.key(r.id) ? true : false);
37692     },
37693
37694     /**
37695      * Returns True if the specified record id is selected.
37696      * @param {String} id The id of record to check
37697      * @return {Boolean}
37698      */
37699     isIdSelected : function(id){
37700         return (this.selections.key(id) ? true : false);
37701     },
37702
37703     // private
37704     handleMouseDown : function(e, t)
37705     {
37706         var view = this.grid.view ? this.grid.view : this.grid;
37707         var rowIndex;
37708         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37709             return;
37710         };
37711         if(e.shiftKey && this.last !== false){
37712             var last = this.last;
37713             this.selectRange(last, rowIndex, e.ctrlKey);
37714             this.last = last; // reset the last
37715             view.focusRow(rowIndex);
37716         }else{
37717             var isSelected = this.isSelected(rowIndex);
37718             if(e.button !== 0 && isSelected){
37719                 view.focusRow(rowIndex);
37720             }else if(e.ctrlKey && isSelected){
37721                 this.deselectRow(rowIndex);
37722             }else if(!isSelected){
37723                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37724                 view.focusRow(rowIndex);
37725             }
37726         }
37727         this.fireEvent("afterselectionchange", this);
37728     },
37729     // private
37730     handleDragableRowClick :  function(grid, rowIndex, e) 
37731     {
37732         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37733             this.selectRow(rowIndex, false);
37734             var view = this.grid.view ? this.grid.view : this.grid;
37735             view.focusRow(rowIndex);
37736              this.fireEvent("afterselectionchange", this);
37737         }
37738     },
37739     
37740     /**
37741      * Selects multiple rows.
37742      * @param {Array} rows Array of the indexes of the row to select
37743      * @param {Boolean} keepExisting (optional) True to keep existing selections
37744      */
37745     selectRows : function(rows, keepExisting){
37746         if(!keepExisting){
37747             this.clearSelections();
37748         }
37749         for(var i = 0, len = rows.length; i < len; i++){
37750             this.selectRow(rows[i], true);
37751         }
37752     },
37753
37754     /**
37755      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37756      * @param {Number} startRow The index of the first row in the range
37757      * @param {Number} endRow The index of the last row in the range
37758      * @param {Boolean} keepExisting (optional) True to retain existing selections
37759      */
37760     selectRange : function(startRow, endRow, keepExisting){
37761         if(this.locked) {
37762             return;
37763         }
37764         if(!keepExisting){
37765             this.clearSelections();
37766         }
37767         if(startRow <= endRow){
37768             for(var i = startRow; i <= endRow; i++){
37769                 this.selectRow(i, true);
37770             }
37771         }else{
37772             for(var i = startRow; i >= endRow; i--){
37773                 this.selectRow(i, true);
37774             }
37775         }
37776     },
37777
37778     /**
37779      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37780      * @param {Number} startRow The index of the first row in the range
37781      * @param {Number} endRow The index of the last row in the range
37782      */
37783     deselectRange : function(startRow, endRow, preventViewNotify){
37784         if(this.locked) {
37785             return;
37786         }
37787         for(var i = startRow; i <= endRow; i++){
37788             this.deselectRow(i, preventViewNotify);
37789         }
37790     },
37791
37792     /**
37793      * Selects a row.
37794      * @param {Number} row The index of the row to select
37795      * @param {Boolean} keepExisting (optional) True to keep existing selections
37796      */
37797     selectRow : function(index, keepExisting, preventViewNotify){
37798         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
37799             return;
37800         }
37801         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37802             if(!keepExisting || this.singleSelect){
37803                 this.clearSelections();
37804             }
37805             var r = this.grid.ds.getAt(index);
37806             this.selections.add(r);
37807             this.last = this.lastActive = index;
37808             if(!preventViewNotify){
37809                 var view = this.grid.view ? this.grid.view : this.grid;
37810                 view.onRowSelect(index);
37811             }
37812             this.fireEvent("rowselect", this, index, r);
37813             this.fireEvent("selectionchange", this);
37814         }
37815     },
37816
37817     /**
37818      * Deselects a row.
37819      * @param {Number} row The index of the row to deselect
37820      */
37821     deselectRow : function(index, preventViewNotify){
37822         if(this.locked) {
37823             return;
37824         }
37825         if(this.last == index){
37826             this.last = false;
37827         }
37828         if(this.lastActive == index){
37829             this.lastActive = false;
37830         }
37831         var r = this.grid.ds.getAt(index);
37832         this.selections.remove(r);
37833         if(!preventViewNotify){
37834             var view = this.grid.view ? this.grid.view : this.grid;
37835             view.onRowDeselect(index);
37836         }
37837         this.fireEvent("rowdeselect", this, index);
37838         this.fireEvent("selectionchange", this);
37839     },
37840
37841     // private
37842     restoreLast : function(){
37843         if(this._last){
37844             this.last = this._last;
37845         }
37846     },
37847
37848     // private
37849     acceptsNav : function(row, col, cm){
37850         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37851     },
37852
37853     // private
37854     onEditorKey : function(field, e){
37855         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37856         if(k == e.TAB){
37857             e.stopEvent();
37858             ed.completeEdit();
37859             if(e.shiftKey){
37860                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37861             }else{
37862                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37863             }
37864         }else if(k == e.ENTER && !e.ctrlKey){
37865             e.stopEvent();
37866             ed.completeEdit();
37867             if(e.shiftKey){
37868                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37869             }else{
37870                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37871             }
37872         }else if(k == e.ESC){
37873             ed.cancelEdit();
37874         }
37875         if(newCell){
37876             g.startEditing(newCell[0], newCell[1]);
37877         }
37878     }
37879 });/*
37880  * Based on:
37881  * Ext JS Library 1.1.1
37882  * Copyright(c) 2006-2007, Ext JS, LLC.
37883  *
37884  * Originally Released Under LGPL - original licence link has changed is not relivant.
37885  *
37886  * Fork - LGPL
37887  * <script type="text/javascript">
37888  */
37889 /**
37890  * @class Roo.grid.CellSelectionModel
37891  * @extends Roo.grid.AbstractSelectionModel
37892  * This class provides the basic implementation for cell selection in a grid.
37893  * @constructor
37894  * @param {Object} config The object containing the configuration of this model.
37895  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37896  */
37897 Roo.grid.CellSelectionModel = function(config){
37898     Roo.apply(this, config);
37899
37900     this.selection = null;
37901
37902     this.addEvents({
37903         /**
37904              * @event beforerowselect
37905              * Fires before a cell is selected.
37906              * @param {SelectionModel} this
37907              * @param {Number} rowIndex The selected row index
37908              * @param {Number} colIndex The selected cell index
37909              */
37910             "beforecellselect" : true,
37911         /**
37912              * @event cellselect
37913              * Fires when a cell is selected.
37914              * @param {SelectionModel} this
37915              * @param {Number} rowIndex The selected row index
37916              * @param {Number} colIndex The selected cell index
37917              */
37918             "cellselect" : true,
37919         /**
37920              * @event selectionchange
37921              * Fires when the active selection changes.
37922              * @param {SelectionModel} this
37923              * @param {Object} selection null for no selection or an object (o) with two properties
37924                 <ul>
37925                 <li>o.record: the record object for the row the selection is in</li>
37926                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37927                 </ul>
37928              */
37929             "selectionchange" : true,
37930         /**
37931              * @event tabend
37932              * Fires when the tab (or enter) was pressed on the last editable cell
37933              * You can use this to trigger add new row.
37934              * @param {SelectionModel} this
37935              */
37936             "tabend" : true,
37937          /**
37938              * @event beforeeditnext
37939              * Fires before the next editable sell is made active
37940              * You can use this to skip to another cell or fire the tabend
37941              *    if you set cell to false
37942              * @param {Object} eventdata object : { cell : [ row, col ] } 
37943              */
37944             "beforeeditnext" : true
37945     });
37946     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37947 };
37948
37949 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37950     
37951     enter_is_tab: false,
37952
37953     /** @ignore */
37954     initEvents : function(){
37955         this.grid.on("mousedown", this.handleMouseDown, this);
37956         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37957         var view = this.grid.view;
37958         view.on("refresh", this.onViewChange, this);
37959         view.on("rowupdated", this.onRowUpdated, this);
37960         view.on("beforerowremoved", this.clearSelections, this);
37961         view.on("beforerowsinserted", this.clearSelections, this);
37962         if(this.grid.isEditor){
37963             this.grid.on("beforeedit", this.beforeEdit,  this);
37964         }
37965     },
37966
37967         //private
37968     beforeEdit : function(e){
37969         this.select(e.row, e.column, false, true, e.record);
37970     },
37971
37972         //private
37973     onRowUpdated : function(v, index, r){
37974         if(this.selection && this.selection.record == r){
37975             v.onCellSelect(index, this.selection.cell[1]);
37976         }
37977     },
37978
37979         //private
37980     onViewChange : function(){
37981         this.clearSelections(true);
37982     },
37983
37984         /**
37985          * Returns the currently selected cell,.
37986          * @return {Array} The selected cell (row, column) or null if none selected.
37987          */
37988     getSelectedCell : function(){
37989         return this.selection ? this.selection.cell : null;
37990     },
37991
37992     /**
37993      * Clears all selections.
37994      * @param {Boolean} true to prevent the gridview from being notified about the change.
37995      */
37996     clearSelections : function(preventNotify){
37997         var s = this.selection;
37998         if(s){
37999             if(preventNotify !== true){
38000                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
38001             }
38002             this.selection = null;
38003             this.fireEvent("selectionchange", this, null);
38004         }
38005     },
38006
38007     /**
38008      * Returns true if there is a selection.
38009      * @return {Boolean}
38010      */
38011     hasSelection : function(){
38012         return this.selection ? true : false;
38013     },
38014
38015     /** @ignore */
38016     handleMouseDown : function(e, t){
38017         var v = this.grid.getView();
38018         if(this.isLocked()){
38019             return;
38020         };
38021         var row = v.findRowIndex(t);
38022         var cell = v.findCellIndex(t);
38023         if(row !== false && cell !== false){
38024             this.select(row, cell);
38025         }
38026     },
38027
38028     /**
38029      * Selects a cell.
38030      * @param {Number} rowIndex
38031      * @param {Number} collIndex
38032      */
38033     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
38034         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
38035             this.clearSelections();
38036             r = r || this.grid.dataSource.getAt(rowIndex);
38037             this.selection = {
38038                 record : r,
38039                 cell : [rowIndex, colIndex]
38040             };
38041             if(!preventViewNotify){
38042                 var v = this.grid.getView();
38043                 v.onCellSelect(rowIndex, colIndex);
38044                 if(preventFocus !== true){
38045                     v.focusCell(rowIndex, colIndex);
38046                 }
38047             }
38048             this.fireEvent("cellselect", this, rowIndex, colIndex);
38049             this.fireEvent("selectionchange", this, this.selection);
38050         }
38051     },
38052
38053         //private
38054     isSelectable : function(rowIndex, colIndex, cm){
38055         return !cm.isHidden(colIndex);
38056     },
38057
38058     /** @ignore */
38059     handleKeyDown : function(e){
38060         //Roo.log('Cell Sel Model handleKeyDown');
38061         if(!e.isNavKeyPress()){
38062             return;
38063         }
38064         var g = this.grid, s = this.selection;
38065         if(!s){
38066             e.stopEvent();
38067             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38068             if(cell){
38069                 this.select(cell[0], cell[1]);
38070             }
38071             return;
38072         }
38073         var sm = this;
38074         var walk = function(row, col, step){
38075             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38076         };
38077         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38078         var newCell;
38079
38080       
38081
38082         switch(k){
38083             case e.TAB:
38084                 // handled by onEditorKey
38085                 if (g.isEditor && g.editing) {
38086                     return;
38087                 }
38088                 if(e.shiftKey) {
38089                     newCell = walk(r, c-1, -1);
38090                 } else {
38091                     newCell = walk(r, c+1, 1);
38092                 }
38093                 break;
38094             
38095             case e.DOWN:
38096                newCell = walk(r+1, c, 1);
38097                 break;
38098             
38099             case e.UP:
38100                 newCell = walk(r-1, c, -1);
38101                 break;
38102             
38103             case e.RIGHT:
38104                 newCell = walk(r, c+1, 1);
38105                 break;
38106             
38107             case e.LEFT:
38108                 newCell = walk(r, c-1, -1);
38109                 break;
38110             
38111             case e.ENTER:
38112                 
38113                 if(g.isEditor && !g.editing){
38114                    g.startEditing(r, c);
38115                    e.stopEvent();
38116                    return;
38117                 }
38118                 
38119                 
38120              break;
38121         };
38122         if(newCell){
38123             this.select(newCell[0], newCell[1]);
38124             e.stopEvent();
38125             
38126         }
38127     },
38128
38129     acceptsNav : function(row, col, cm){
38130         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38131     },
38132     /**
38133      * Selects a cell.
38134      * @param {Number} field (not used) - as it's normally used as a listener
38135      * @param {Number} e - event - fake it by using
38136      *
38137      * var e = Roo.EventObjectImpl.prototype;
38138      * e.keyCode = e.TAB
38139      *
38140      * 
38141      */
38142     onEditorKey : function(field, e){
38143         
38144         var k = e.getKey(),
38145             newCell,
38146             g = this.grid,
38147             ed = g.activeEditor,
38148             forward = false;
38149         ///Roo.log('onEditorKey' + k);
38150         
38151         
38152         if (this.enter_is_tab && k == e.ENTER) {
38153             k = e.TAB;
38154         }
38155         
38156         if(k == e.TAB){
38157             if(e.shiftKey){
38158                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38159             }else{
38160                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38161                 forward = true;
38162             }
38163             
38164             e.stopEvent();
38165             
38166         } else if(k == e.ENTER &&  !e.ctrlKey){
38167             ed.completeEdit();
38168             e.stopEvent();
38169             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38170         
38171                 } else if(k == e.ESC){
38172             ed.cancelEdit();
38173         }
38174                 
38175         if (newCell) {
38176             var ecall = { cell : newCell, forward : forward };
38177             this.fireEvent('beforeeditnext', ecall );
38178             newCell = ecall.cell;
38179                         forward = ecall.forward;
38180         }
38181                 
38182         if(newCell){
38183             //Roo.log('next cell after edit');
38184             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38185         } else if (forward) {
38186             // tabbed past last
38187             this.fireEvent.defer(100, this, ['tabend',this]);
38188         }
38189     }
38190 });/*
38191  * Based on:
38192  * Ext JS Library 1.1.1
38193  * Copyright(c) 2006-2007, Ext JS, LLC.
38194  *
38195  * Originally Released Under LGPL - original licence link has changed is not relivant.
38196  *
38197  * Fork - LGPL
38198  * <script type="text/javascript">
38199  */
38200  
38201 /**
38202  * @class Roo.grid.EditorGrid
38203  * @extends Roo.grid.Grid
38204  * Class for creating and editable grid.
38205  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38206  * The container MUST have some type of size defined for the grid to fill. The container will be 
38207  * automatically set to position relative if it isn't already.
38208  * @param {Object} dataSource The data model to bind to
38209  * @param {Object} colModel The column model with info about this grid's columns
38210  */
38211 Roo.grid.EditorGrid = function(container, config){
38212     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38213     this.getGridEl().addClass("xedit-grid");
38214
38215     if(!this.selModel){
38216         this.selModel = new Roo.grid.CellSelectionModel();
38217     }
38218
38219     this.activeEditor = null;
38220
38221         this.addEvents({
38222             /**
38223              * @event beforeedit
38224              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38225              * <ul style="padding:5px;padding-left:16px;">
38226              * <li>grid - This grid</li>
38227              * <li>record - The record being edited</li>
38228              * <li>field - The field name being edited</li>
38229              * <li>value - The value for the field being edited.</li>
38230              * <li>row - The grid row index</li>
38231              * <li>column - The grid column index</li>
38232              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38233              * </ul>
38234              * @param {Object} e An edit event (see above for description)
38235              */
38236             "beforeedit" : true,
38237             /**
38238              * @event afteredit
38239              * Fires after a cell is edited. <br />
38240              * <ul style="padding:5px;padding-left:16px;">
38241              * <li>grid - This grid</li>
38242              * <li>record - The record being edited</li>
38243              * <li>field - The field name being edited</li>
38244              * <li>value - The value being set</li>
38245              * <li>originalValue - The original value for the field, before the edit.</li>
38246              * <li>row - The grid row index</li>
38247              * <li>column - The grid column index</li>
38248              * </ul>
38249              * @param {Object} e An edit event (see above for description)
38250              */
38251             "afteredit" : true,
38252             /**
38253              * @event validateedit
38254              * Fires after a cell is edited, but before the value is set in the record. 
38255          * You can use this to modify the value being set in the field, Return false
38256              * to cancel the change. The edit event object has the following properties <br />
38257              * <ul style="padding:5px;padding-left:16px;">
38258          * <li>editor - This editor</li>
38259              * <li>grid - This grid</li>
38260              * <li>record - The record being edited</li>
38261              * <li>field - The field name being edited</li>
38262              * <li>value - The value being set</li>
38263              * <li>originalValue - The original value for the field, before the edit.</li>
38264              * <li>row - The grid row index</li>
38265              * <li>column - The grid column index</li>
38266              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38267              * </ul>
38268              * @param {Object} e An edit event (see above for description)
38269              */
38270             "validateedit" : true
38271         });
38272     this.on("bodyscroll", this.stopEditing,  this);
38273     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38274 };
38275
38276 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38277     /**
38278      * @cfg {Number} clicksToEdit
38279      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38280      */
38281     clicksToEdit: 2,
38282
38283     // private
38284     isEditor : true,
38285     // private
38286     trackMouseOver: false, // causes very odd FF errors
38287
38288     onCellDblClick : function(g, row, col){
38289         this.startEditing(row, col);
38290     },
38291
38292     onEditComplete : function(ed, value, startValue){
38293         this.editing = false;
38294         this.activeEditor = null;
38295         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
38296         var r = ed.record;
38297         var field = this.colModel.getDataIndex(ed.col);
38298         var e = {
38299             grid: this,
38300             record: r,
38301             field: field,
38302             originalValue: startValue,
38303             value: value,
38304             row: ed.row,
38305             column: ed.col,
38306             cancel:false,
38307             editor: ed
38308         };
38309         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
38310         cell.show();
38311           
38312         if(String(value) !== String(startValue)){
38313             
38314             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
38315                 r.set(field, e.value);
38316                 // if we are dealing with a combo box..
38317                 // then we also set the 'name' colum to be the displayField
38318                 if (ed.field.displayField && ed.field.name) {
38319                     r.set(ed.field.name, ed.field.el.dom.value);
38320                 }
38321                 
38322                 delete e.cancel; //?? why!!!
38323                 this.fireEvent("afteredit", e);
38324             }
38325         } else {
38326             this.fireEvent("afteredit", e); // always fire it!
38327         }
38328         this.view.focusCell(ed.row, ed.col);
38329     },
38330
38331     /**
38332      * Starts editing the specified for the specified row/column
38333      * @param {Number} rowIndex
38334      * @param {Number} colIndex
38335      */
38336     startEditing : function(row, col){
38337         this.stopEditing();
38338         if(this.colModel.isCellEditable(col, row)){
38339             this.view.ensureVisible(row, col, true);
38340           
38341             var r = this.dataSource.getAt(row);
38342             var field = this.colModel.getDataIndex(col);
38343             var cell = Roo.get(this.view.getCell(row,col));
38344             var e = {
38345                 grid: this,
38346                 record: r,
38347                 field: field,
38348                 value: r.data[field],
38349                 row: row,
38350                 column: col,
38351                 cancel:false 
38352             };
38353             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
38354                 this.editing = true;
38355                 var ed = this.colModel.getCellEditor(col, row);
38356                 
38357                 if (!ed) {
38358                     return;
38359                 }
38360                 if(!ed.rendered){
38361                     ed.render(ed.parentEl || document.body);
38362                 }
38363                 ed.field.reset();
38364                
38365                 cell.hide();
38366                 
38367                 (function(){ // complex but required for focus issues in safari, ie and opera
38368                     ed.row = row;
38369                     ed.col = col;
38370                     ed.record = r;
38371                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
38372                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
38373                     this.activeEditor = ed;
38374                     var v = r.data[field];
38375                     ed.startEdit(this.view.getCell(row, col), v);
38376                     // combo's with 'displayField and name set
38377                     if (ed.field.displayField && ed.field.name) {
38378                         ed.field.el.dom.value = r.data[ed.field.name];
38379                     }
38380                     
38381                     
38382                 }).defer(50, this);
38383             }
38384         }
38385     },
38386         
38387     /**
38388      * Stops any active editing
38389      */
38390     stopEditing : function(){
38391         if(this.activeEditor){
38392             this.activeEditor.completeEdit();
38393         }
38394         this.activeEditor = null;
38395     },
38396         
38397          /**
38398      * Called to get grid's drag proxy text, by default returns this.ddText.
38399      * @return {String}
38400      */
38401     getDragDropText : function(){
38402         var count = this.selModel.getSelectedCell() ? 1 : 0;
38403         return String.format(this.ddText, count, count == 1 ? '' : 's');
38404     }
38405         
38406 });/*
38407  * Based on:
38408  * Ext JS Library 1.1.1
38409  * Copyright(c) 2006-2007, Ext JS, LLC.
38410  *
38411  * Originally Released Under LGPL - original licence link has changed is not relivant.
38412  *
38413  * Fork - LGPL
38414  * <script type="text/javascript">
38415  */
38416
38417 // private - not really -- you end up using it !
38418 // This is a support class used internally by the Grid components
38419
38420 /**
38421  * @class Roo.grid.GridEditor
38422  * @extends Roo.Editor
38423  * Class for creating and editable grid elements.
38424  * @param {Object} config any settings (must include field)
38425  */
38426 Roo.grid.GridEditor = function(field, config){
38427     if (!config && field.field) {
38428         config = field;
38429         field = Roo.factory(config.field, Roo.form);
38430     }
38431     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
38432     field.monitorTab = false;
38433 };
38434
38435 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
38436     
38437     /**
38438      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
38439      */
38440     
38441     alignment: "tl-tl",
38442     autoSize: "width",
38443     hideEl : false,
38444     cls: "x-small-editor x-grid-editor",
38445     shim:false,
38446     shadow:"frame"
38447 });/*
38448  * Based on:
38449  * Ext JS Library 1.1.1
38450  * Copyright(c) 2006-2007, Ext JS, LLC.
38451  *
38452  * Originally Released Under LGPL - original licence link has changed is not relivant.
38453  *
38454  * Fork - LGPL
38455  * <script type="text/javascript">
38456  */
38457   
38458
38459   
38460 Roo.grid.PropertyRecord = Roo.data.Record.create([
38461     {name:'name',type:'string'},  'value'
38462 ]);
38463
38464
38465 Roo.grid.PropertyStore = function(grid, source){
38466     this.grid = grid;
38467     this.store = new Roo.data.Store({
38468         recordType : Roo.grid.PropertyRecord
38469     });
38470     this.store.on('update', this.onUpdate,  this);
38471     if(source){
38472         this.setSource(source);
38473     }
38474     Roo.grid.PropertyStore.superclass.constructor.call(this);
38475 };
38476
38477
38478
38479 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38480     setSource : function(o){
38481         this.source = o;
38482         this.store.removeAll();
38483         var data = [];
38484         for(var k in o){
38485             if(this.isEditableValue(o[k])){
38486                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38487             }
38488         }
38489         this.store.loadRecords({records: data}, {}, true);
38490     },
38491
38492     onUpdate : function(ds, record, type){
38493         if(type == Roo.data.Record.EDIT){
38494             var v = record.data['value'];
38495             var oldValue = record.modified['value'];
38496             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38497                 this.source[record.id] = v;
38498                 record.commit();
38499                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38500             }else{
38501                 record.reject();
38502             }
38503         }
38504     },
38505
38506     getProperty : function(row){
38507        return this.store.getAt(row);
38508     },
38509
38510     isEditableValue: function(val){
38511         if(val && val instanceof Date){
38512             return true;
38513         }else if(typeof val == 'object' || typeof val == 'function'){
38514             return false;
38515         }
38516         return true;
38517     },
38518
38519     setValue : function(prop, value){
38520         this.source[prop] = value;
38521         this.store.getById(prop).set('value', value);
38522     },
38523
38524     getSource : function(){
38525         return this.source;
38526     }
38527 });
38528
38529 Roo.grid.PropertyColumnModel = function(grid, store){
38530     this.grid = grid;
38531     var g = Roo.grid;
38532     g.PropertyColumnModel.superclass.constructor.call(this, [
38533         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38534         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38535     ]);
38536     this.store = store;
38537     this.bselect = Roo.DomHelper.append(document.body, {
38538         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38539             {tag: 'option', value: 'true', html: 'true'},
38540             {tag: 'option', value: 'false', html: 'false'}
38541         ]
38542     });
38543     Roo.id(this.bselect);
38544     var f = Roo.form;
38545     this.editors = {
38546         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38547         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38548         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38549         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38550         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38551     };
38552     this.renderCellDelegate = this.renderCell.createDelegate(this);
38553     this.renderPropDelegate = this.renderProp.createDelegate(this);
38554 };
38555
38556 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38557     
38558     
38559     nameText : 'Name',
38560     valueText : 'Value',
38561     
38562     dateFormat : 'm/j/Y',
38563     
38564     
38565     renderDate : function(dateVal){
38566         return dateVal.dateFormat(this.dateFormat);
38567     },
38568
38569     renderBool : function(bVal){
38570         return bVal ? 'true' : 'false';
38571     },
38572
38573     isCellEditable : function(colIndex, rowIndex){
38574         return colIndex == 1;
38575     },
38576
38577     getRenderer : function(col){
38578         return col == 1 ?
38579             this.renderCellDelegate : this.renderPropDelegate;
38580     },
38581
38582     renderProp : function(v){
38583         return this.getPropertyName(v);
38584     },
38585
38586     renderCell : function(val){
38587         var rv = val;
38588         if(val instanceof Date){
38589             rv = this.renderDate(val);
38590         }else if(typeof val == 'boolean'){
38591             rv = this.renderBool(val);
38592         }
38593         return Roo.util.Format.htmlEncode(rv);
38594     },
38595
38596     getPropertyName : function(name){
38597         var pn = this.grid.propertyNames;
38598         return pn && pn[name] ? pn[name] : name;
38599     },
38600
38601     getCellEditor : function(colIndex, rowIndex){
38602         var p = this.store.getProperty(rowIndex);
38603         var n = p.data['name'], val = p.data['value'];
38604         
38605         if(typeof(this.grid.customEditors[n]) == 'string'){
38606             return this.editors[this.grid.customEditors[n]];
38607         }
38608         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38609             return this.grid.customEditors[n];
38610         }
38611         if(val instanceof Date){
38612             return this.editors['date'];
38613         }else if(typeof val == 'number'){
38614             return this.editors['number'];
38615         }else if(typeof val == 'boolean'){
38616             return this.editors['boolean'];
38617         }else{
38618             return this.editors['string'];
38619         }
38620     }
38621 });
38622
38623 /**
38624  * @class Roo.grid.PropertyGrid
38625  * @extends Roo.grid.EditorGrid
38626  * This class represents the  interface of a component based property grid control.
38627  * <br><br>Usage:<pre><code>
38628  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38629       
38630  });
38631  // set any options
38632  grid.render();
38633  * </code></pre>
38634   
38635  * @constructor
38636  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38637  * The container MUST have some type of size defined for the grid to fill. The container will be
38638  * automatically set to position relative if it isn't already.
38639  * @param {Object} config A config object that sets properties on this grid.
38640  */
38641 Roo.grid.PropertyGrid = function(container, config){
38642     config = config || {};
38643     var store = new Roo.grid.PropertyStore(this);
38644     this.store = store;
38645     var cm = new Roo.grid.PropertyColumnModel(this, store);
38646     store.store.sort('name', 'ASC');
38647     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38648         ds: store.store,
38649         cm: cm,
38650         enableColLock:false,
38651         enableColumnMove:false,
38652         stripeRows:false,
38653         trackMouseOver: false,
38654         clicksToEdit:1
38655     }, config));
38656     this.getGridEl().addClass('x-props-grid');
38657     this.lastEditRow = null;
38658     this.on('columnresize', this.onColumnResize, this);
38659     this.addEvents({
38660          /**
38661              * @event beforepropertychange
38662              * Fires before a property changes (return false to stop?)
38663              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38664              * @param {String} id Record Id
38665              * @param {String} newval New Value
38666          * @param {String} oldval Old Value
38667              */
38668         "beforepropertychange": true,
38669         /**
38670              * @event propertychange
38671              * Fires after a property changes
38672              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38673              * @param {String} id Record Id
38674              * @param {String} newval New Value
38675          * @param {String} oldval Old Value
38676              */
38677         "propertychange": true
38678     });
38679     this.customEditors = this.customEditors || {};
38680 };
38681 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38682     
38683      /**
38684      * @cfg {Object} customEditors map of colnames=> custom editors.
38685      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38686      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38687      * false disables editing of the field.
38688          */
38689     
38690       /**
38691      * @cfg {Object} propertyNames map of property Names to their displayed value
38692          */
38693     
38694     render : function(){
38695         Roo.grid.PropertyGrid.superclass.render.call(this);
38696         this.autoSize.defer(100, this);
38697     },
38698
38699     autoSize : function(){
38700         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38701         if(this.view){
38702             this.view.fitColumns();
38703         }
38704     },
38705
38706     onColumnResize : function(){
38707         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38708         this.autoSize();
38709     },
38710     /**
38711      * Sets the data for the Grid
38712      * accepts a Key => Value object of all the elements avaiable.
38713      * @param {Object} data  to appear in grid.
38714      */
38715     setSource : function(source){
38716         this.store.setSource(source);
38717         //this.autoSize();
38718     },
38719     /**
38720      * Gets all the data from the grid.
38721      * @return {Object} data  data stored in grid
38722      */
38723     getSource : function(){
38724         return this.store.getSource();
38725     }
38726 });/*
38727   
38728  * Licence LGPL
38729  
38730  */
38731  
38732 /**
38733  * @class Roo.grid.Calendar
38734  * @extends Roo.grid.Grid
38735  * This class extends the Grid to provide a calendar widget
38736  * <br><br>Usage:<pre><code>
38737  var grid = new Roo.grid.Calendar("my-container-id", {
38738      ds: myDataStore,
38739      cm: myColModel,
38740      selModel: mySelectionModel,
38741      autoSizeColumns: true,
38742      monitorWindowResize: false,
38743      trackMouseOver: true
38744      eventstore : real data store..
38745  });
38746  // set any options
38747  grid.render();
38748   
38749   * @constructor
38750  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38751  * The container MUST have some type of size defined for the grid to fill. The container will be
38752  * automatically set to position relative if it isn't already.
38753  * @param {Object} config A config object that sets properties on this grid.
38754  */
38755 Roo.grid.Calendar = function(container, config){
38756         // initialize the container
38757         this.container = Roo.get(container);
38758         this.container.update("");
38759         this.container.setStyle("overflow", "hidden");
38760     this.container.addClass('x-grid-container');
38761
38762     this.id = this.container.id;
38763
38764     Roo.apply(this, config);
38765     // check and correct shorthanded configs
38766     
38767     var rows = [];
38768     var d =1;
38769     for (var r = 0;r < 6;r++) {
38770         
38771         rows[r]=[];
38772         for (var c =0;c < 7;c++) {
38773             rows[r][c]= '';
38774         }
38775     }
38776     if (this.eventStore) {
38777         this.eventStore= Roo.factory(this.eventStore, Roo.data);
38778         this.eventStore.on('load',this.onLoad, this);
38779         this.eventStore.on('beforeload',this.clearEvents, this);
38780          
38781     }
38782     
38783     this.dataSource = new Roo.data.Store({
38784             proxy: new Roo.data.MemoryProxy(rows),
38785             reader: new Roo.data.ArrayReader({}, [
38786                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
38787     });
38788
38789     this.dataSource.load();
38790     this.ds = this.dataSource;
38791     this.ds.xmodule = this.xmodule || false;
38792     
38793     
38794     var cellRender = function(v,x,r)
38795     {
38796         return String.format(
38797             '<div class="fc-day  fc-widget-content"><div>' +
38798                 '<div class="fc-event-container"></div>' +
38799                 '<div class="fc-day-number">{0}</div>'+
38800                 
38801                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
38802             '</div></div>', v);
38803     
38804     }
38805     
38806     
38807     this.colModel = new Roo.grid.ColumnModel( [
38808         {
38809             xtype: 'ColumnModel',
38810             xns: Roo.grid,
38811             dataIndex : 'weekday0',
38812             header : 'Sunday',
38813             renderer : cellRender
38814         },
38815         {
38816             xtype: 'ColumnModel',
38817             xns: Roo.grid,
38818             dataIndex : 'weekday1',
38819             header : 'Monday',
38820             renderer : cellRender
38821         },
38822         {
38823             xtype: 'ColumnModel',
38824             xns: Roo.grid,
38825             dataIndex : 'weekday2',
38826             header : 'Tuesday',
38827             renderer : cellRender
38828         },
38829         {
38830             xtype: 'ColumnModel',
38831             xns: Roo.grid,
38832             dataIndex : 'weekday3',
38833             header : 'Wednesday',
38834             renderer : cellRender
38835         },
38836         {
38837             xtype: 'ColumnModel',
38838             xns: Roo.grid,
38839             dataIndex : 'weekday4',
38840             header : 'Thursday',
38841             renderer : cellRender
38842         },
38843         {
38844             xtype: 'ColumnModel',
38845             xns: Roo.grid,
38846             dataIndex : 'weekday5',
38847             header : 'Friday',
38848             renderer : cellRender
38849         },
38850         {
38851             xtype: 'ColumnModel',
38852             xns: Roo.grid,
38853             dataIndex : 'weekday6',
38854             header : 'Saturday',
38855             renderer : cellRender
38856         }
38857     ]);
38858     this.cm = this.colModel;
38859     this.cm.xmodule = this.xmodule || false;
38860  
38861         
38862           
38863     //this.selModel = new Roo.grid.CellSelectionModel();
38864     //this.sm = this.selModel;
38865     //this.selModel.init(this);
38866     
38867     
38868     if(this.width){
38869         this.container.setWidth(this.width);
38870     }
38871
38872     if(this.height){
38873         this.container.setHeight(this.height);
38874     }
38875     /** @private */
38876         this.addEvents({
38877         // raw events
38878         /**
38879          * @event click
38880          * The raw click event for the entire grid.
38881          * @param {Roo.EventObject} e
38882          */
38883         "click" : true,
38884         /**
38885          * @event dblclick
38886          * The raw dblclick event for the entire grid.
38887          * @param {Roo.EventObject} e
38888          */
38889         "dblclick" : true,
38890         /**
38891          * @event contextmenu
38892          * The raw contextmenu event for the entire grid.
38893          * @param {Roo.EventObject} e
38894          */
38895         "contextmenu" : true,
38896         /**
38897          * @event mousedown
38898          * The raw mousedown event for the entire grid.
38899          * @param {Roo.EventObject} e
38900          */
38901         "mousedown" : true,
38902         /**
38903          * @event mouseup
38904          * The raw mouseup event for the entire grid.
38905          * @param {Roo.EventObject} e
38906          */
38907         "mouseup" : true,
38908         /**
38909          * @event mouseover
38910          * The raw mouseover event for the entire grid.
38911          * @param {Roo.EventObject} e
38912          */
38913         "mouseover" : true,
38914         /**
38915          * @event mouseout
38916          * The raw mouseout event for the entire grid.
38917          * @param {Roo.EventObject} e
38918          */
38919         "mouseout" : true,
38920         /**
38921          * @event keypress
38922          * The raw keypress event for the entire grid.
38923          * @param {Roo.EventObject} e
38924          */
38925         "keypress" : true,
38926         /**
38927          * @event keydown
38928          * The raw keydown event for the entire grid.
38929          * @param {Roo.EventObject} e
38930          */
38931         "keydown" : true,
38932
38933         // custom events
38934
38935         /**
38936          * @event cellclick
38937          * Fires when a cell is clicked
38938          * @param {Grid} this
38939          * @param {Number} rowIndex
38940          * @param {Number} columnIndex
38941          * @param {Roo.EventObject} e
38942          */
38943         "cellclick" : true,
38944         /**
38945          * @event celldblclick
38946          * Fires when a cell is double clicked
38947          * @param {Grid} this
38948          * @param {Number} rowIndex
38949          * @param {Number} columnIndex
38950          * @param {Roo.EventObject} e
38951          */
38952         "celldblclick" : true,
38953         /**
38954          * @event rowclick
38955          * Fires when a row is clicked
38956          * @param {Grid} this
38957          * @param {Number} rowIndex
38958          * @param {Roo.EventObject} e
38959          */
38960         "rowclick" : true,
38961         /**
38962          * @event rowdblclick
38963          * Fires when a row is double clicked
38964          * @param {Grid} this
38965          * @param {Number} rowIndex
38966          * @param {Roo.EventObject} e
38967          */
38968         "rowdblclick" : true,
38969         /**
38970          * @event headerclick
38971          * Fires when a header is clicked
38972          * @param {Grid} this
38973          * @param {Number} columnIndex
38974          * @param {Roo.EventObject} e
38975          */
38976         "headerclick" : true,
38977         /**
38978          * @event headerdblclick
38979          * Fires when a header cell is double clicked
38980          * @param {Grid} this
38981          * @param {Number} columnIndex
38982          * @param {Roo.EventObject} e
38983          */
38984         "headerdblclick" : true,
38985         /**
38986          * @event rowcontextmenu
38987          * Fires when a row is right clicked
38988          * @param {Grid} this
38989          * @param {Number} rowIndex
38990          * @param {Roo.EventObject} e
38991          */
38992         "rowcontextmenu" : true,
38993         /**
38994          * @event cellcontextmenu
38995          * Fires when a cell is right clicked
38996          * @param {Grid} this
38997          * @param {Number} rowIndex
38998          * @param {Number} cellIndex
38999          * @param {Roo.EventObject} e
39000          */
39001          "cellcontextmenu" : true,
39002         /**
39003          * @event headercontextmenu
39004          * Fires when a header is right clicked
39005          * @param {Grid} this
39006          * @param {Number} columnIndex
39007          * @param {Roo.EventObject} e
39008          */
39009         "headercontextmenu" : true,
39010         /**
39011          * @event bodyscroll
39012          * Fires when the body element is scrolled
39013          * @param {Number} scrollLeft
39014          * @param {Number} scrollTop
39015          */
39016         "bodyscroll" : true,
39017         /**
39018          * @event columnresize
39019          * Fires when the user resizes a column
39020          * @param {Number} columnIndex
39021          * @param {Number} newSize
39022          */
39023         "columnresize" : true,
39024         /**
39025          * @event columnmove
39026          * Fires when the user moves a column
39027          * @param {Number} oldIndex
39028          * @param {Number} newIndex
39029          */
39030         "columnmove" : true,
39031         /**
39032          * @event startdrag
39033          * Fires when row(s) start being dragged
39034          * @param {Grid} this
39035          * @param {Roo.GridDD} dd The drag drop object
39036          * @param {event} e The raw browser event
39037          */
39038         "startdrag" : true,
39039         /**
39040          * @event enddrag
39041          * Fires when a drag operation is complete
39042          * @param {Grid} this
39043          * @param {Roo.GridDD} dd The drag drop object
39044          * @param {event} e The raw browser event
39045          */
39046         "enddrag" : true,
39047         /**
39048          * @event dragdrop
39049          * Fires when dragged row(s) are dropped on a valid DD target
39050          * @param {Grid} this
39051          * @param {Roo.GridDD} dd The drag drop object
39052          * @param {String} targetId The target drag drop object
39053          * @param {event} e The raw browser event
39054          */
39055         "dragdrop" : true,
39056         /**
39057          * @event dragover
39058          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
39059          * @param {Grid} this
39060          * @param {Roo.GridDD} dd The drag drop object
39061          * @param {String} targetId The target drag drop object
39062          * @param {event} e The raw browser event
39063          */
39064         "dragover" : true,
39065         /**
39066          * @event dragenter
39067          *  Fires when the dragged row(s) first cross another DD target while being dragged
39068          * @param {Grid} this
39069          * @param {Roo.GridDD} dd The drag drop object
39070          * @param {String} targetId The target drag drop object
39071          * @param {event} e The raw browser event
39072          */
39073         "dragenter" : true,
39074         /**
39075          * @event dragout
39076          * Fires when the dragged row(s) leave another DD target while being dragged
39077          * @param {Grid} this
39078          * @param {Roo.GridDD} dd The drag drop object
39079          * @param {String} targetId The target drag drop object
39080          * @param {event} e The raw browser event
39081          */
39082         "dragout" : true,
39083         /**
39084          * @event rowclass
39085          * Fires when a row is rendered, so you can change add a style to it.
39086          * @param {GridView} gridview   The grid view
39087          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
39088          */
39089         'rowclass' : true,
39090
39091         /**
39092          * @event render
39093          * Fires when the grid is rendered
39094          * @param {Grid} grid
39095          */
39096         'render' : true,
39097             /**
39098              * @event select
39099              * Fires when a date is selected
39100              * @param {DatePicker} this
39101              * @param {Date} date The selected date
39102              */
39103         'select': true,
39104         /**
39105              * @event monthchange
39106              * Fires when the displayed month changes 
39107              * @param {DatePicker} this
39108              * @param {Date} date The selected month
39109              */
39110         'monthchange': true,
39111         /**
39112              * @event evententer
39113              * Fires when mouse over an event
39114              * @param {Calendar} this
39115              * @param {event} Event
39116              */
39117         'evententer': true,
39118         /**
39119              * @event eventleave
39120              * Fires when the mouse leaves an
39121              * @param {Calendar} this
39122              * @param {event}
39123              */
39124         'eventleave': true,
39125         /**
39126              * @event eventclick
39127              * Fires when the mouse click an
39128              * @param {Calendar} this
39129              * @param {event}
39130              */
39131         'eventclick': true,
39132         /**
39133              * @event eventrender
39134              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
39135              * @param {Calendar} this
39136              * @param {data} data to be modified
39137              */
39138         'eventrender': true
39139         
39140     });
39141
39142     Roo.grid.Grid.superclass.constructor.call(this);
39143     this.on('render', function() {
39144         this.view.el.addClass('x-grid-cal'); 
39145         
39146         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
39147
39148     },this);
39149     
39150     if (!Roo.grid.Calendar.style) {
39151         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
39152             
39153             
39154             '.x-grid-cal .x-grid-col' :  {
39155                 height: 'auto !important',
39156                 'vertical-align': 'top'
39157             },
39158             '.x-grid-cal  .fc-event-hori' : {
39159                 height: '14px'
39160             }
39161              
39162             
39163         }, Roo.id());
39164     }
39165
39166     
39167     
39168 };
39169 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
39170     /**
39171      * @cfg {Store} eventStore The store that loads events.
39172      */
39173     eventStore : 25,
39174
39175      
39176     activeDate : false,
39177     startDay : 0,
39178     autoWidth : true,
39179     monitorWindowResize : false,
39180
39181     
39182     resizeColumns : function() {
39183         var col = (this.view.el.getWidth() / 7) - 3;
39184         // loop through cols, and setWidth
39185         for(var i =0 ; i < 7 ; i++){
39186             this.cm.setColumnWidth(i, col);
39187         }
39188     },
39189      setDate :function(date) {
39190         
39191         Roo.log('setDate?');
39192         
39193         this.resizeColumns();
39194         var vd = this.activeDate;
39195         this.activeDate = date;
39196 //        if(vd && this.el){
39197 //            var t = date.getTime();
39198 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
39199 //                Roo.log('using add remove');
39200 //                
39201 //                this.fireEvent('monthchange', this, date);
39202 //                
39203 //                this.cells.removeClass("fc-state-highlight");
39204 //                this.cells.each(function(c){
39205 //                   if(c.dateValue == t){
39206 //                       c.addClass("fc-state-highlight");
39207 //                       setTimeout(function(){
39208 //                            try{c.dom.firstChild.focus();}catch(e){}
39209 //                       }, 50);
39210 //                       return false;
39211 //                   }
39212 //                   return true;
39213 //                });
39214 //                return;
39215 //            }
39216 //        }
39217         
39218         var days = date.getDaysInMonth();
39219         
39220         var firstOfMonth = date.getFirstDateOfMonth();
39221         var startingPos = firstOfMonth.getDay()-this.startDay;
39222         
39223         if(startingPos < this.startDay){
39224             startingPos += 7;
39225         }
39226         
39227         var pm = date.add(Date.MONTH, -1);
39228         var prevStart = pm.getDaysInMonth()-startingPos;
39229 //        
39230         
39231         
39232         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
39233         
39234         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
39235         //this.cells.addClassOnOver('fc-state-hover');
39236         
39237         var cells = this.cells.elements;
39238         var textEls = this.textNodes;
39239         
39240         //Roo.each(cells, function(cell){
39241         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
39242         //});
39243         
39244         days += startingPos;
39245
39246         // convert everything to numbers so it's fast
39247         var day = 86400000;
39248         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
39249         //Roo.log(d);
39250         //Roo.log(pm);
39251         //Roo.log(prevStart);
39252         
39253         var today = new Date().clearTime().getTime();
39254         var sel = date.clearTime().getTime();
39255         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
39256         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
39257         var ddMatch = this.disabledDatesRE;
39258         var ddText = this.disabledDatesText;
39259         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
39260         var ddaysText = this.disabledDaysText;
39261         var format = this.format;
39262         
39263         var setCellClass = function(cal, cell){
39264             
39265             //Roo.log('set Cell Class');
39266             cell.title = "";
39267             var t = d.getTime();
39268             
39269             //Roo.log(d);
39270             
39271             
39272             cell.dateValue = t;
39273             if(t == today){
39274                 cell.className += " fc-today";
39275                 cell.className += " fc-state-highlight";
39276                 cell.title = cal.todayText;
39277             }
39278             if(t == sel){
39279                 // disable highlight in other month..
39280                 cell.className += " fc-state-highlight";
39281                 
39282             }
39283             // disabling
39284             if(t < min) {
39285                 //cell.className = " fc-state-disabled";
39286                 cell.title = cal.minText;
39287                 return;
39288             }
39289             if(t > max) {
39290                 //cell.className = " fc-state-disabled";
39291                 cell.title = cal.maxText;
39292                 return;
39293             }
39294             if(ddays){
39295                 if(ddays.indexOf(d.getDay()) != -1){
39296                     // cell.title = ddaysText;
39297                    // cell.className = " fc-state-disabled";
39298                 }
39299             }
39300             if(ddMatch && format){
39301                 var fvalue = d.dateFormat(format);
39302                 if(ddMatch.test(fvalue)){
39303                     cell.title = ddText.replace("%0", fvalue);
39304                    cell.className = " fc-state-disabled";
39305                 }
39306             }
39307             
39308             if (!cell.initialClassName) {
39309                 cell.initialClassName = cell.dom.className;
39310             }
39311             
39312             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
39313         };
39314
39315         var i = 0;
39316         
39317         for(; i < startingPos; i++) {
39318             cells[i].dayName =  (++prevStart);
39319             Roo.log(textEls[i]);
39320             d.setDate(d.getDate()+1);
39321             
39322             //cells[i].className = "fc-past fc-other-month";
39323             setCellClass(this, cells[i]);
39324         }
39325         
39326         var intDay = 0;
39327         
39328         for(; i < days; i++){
39329             intDay = i - startingPos + 1;
39330             cells[i].dayName =  (intDay);
39331             d.setDate(d.getDate()+1);
39332             
39333             cells[i].className = ''; // "x-date-active";
39334             setCellClass(this, cells[i]);
39335         }
39336         var extraDays = 0;
39337         
39338         for(; i < 42; i++) {
39339             //textEls[i].innerHTML = (++extraDays);
39340             
39341             d.setDate(d.getDate()+1);
39342             cells[i].dayName = (++extraDays);
39343             cells[i].className = "fc-future fc-other-month";
39344             setCellClass(this, cells[i]);
39345         }
39346         
39347         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
39348         
39349         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
39350         
39351         // this will cause all the cells to mis
39352         var rows= [];
39353         var i =0;
39354         for (var r = 0;r < 6;r++) {
39355             for (var c =0;c < 7;c++) {
39356                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
39357             }    
39358         }
39359         
39360         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
39361         for(i=0;i<cells.length;i++) {
39362             
39363             this.cells.elements[i].dayName = cells[i].dayName ;
39364             this.cells.elements[i].className = cells[i].className;
39365             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
39366             this.cells.elements[i].title = cells[i].title ;
39367             this.cells.elements[i].dateValue = cells[i].dateValue ;
39368         }
39369         
39370         
39371         
39372         
39373         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
39374         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
39375         
39376         ////if(totalRows != 6){
39377             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
39378            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
39379        // }
39380         
39381         this.fireEvent('monthchange', this, date);
39382         
39383         
39384     },
39385  /**
39386      * Returns the grid's SelectionModel.
39387      * @return {SelectionModel}
39388      */
39389     getSelectionModel : function(){
39390         if(!this.selModel){
39391             this.selModel = new Roo.grid.CellSelectionModel();
39392         }
39393         return this.selModel;
39394     },
39395
39396     load: function() {
39397         this.eventStore.load()
39398         
39399         
39400         
39401     },
39402     
39403     findCell : function(dt) {
39404         dt = dt.clearTime().getTime();
39405         var ret = false;
39406         this.cells.each(function(c){
39407             //Roo.log("check " +c.dateValue + '?=' + dt);
39408             if(c.dateValue == dt){
39409                 ret = c;
39410                 return false;
39411             }
39412             return true;
39413         });
39414         
39415         return ret;
39416     },
39417     
39418     findCells : function(rec) {
39419         var s = rec.data.start_dt.clone().clearTime().getTime();
39420        // Roo.log(s);
39421         var e= rec.data.end_dt.clone().clearTime().getTime();
39422        // Roo.log(e);
39423         var ret = [];
39424         this.cells.each(function(c){
39425              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
39426             
39427             if(c.dateValue > e){
39428                 return ;
39429             }
39430             if(c.dateValue < s){
39431                 return ;
39432             }
39433             ret.push(c);
39434         });
39435         
39436         return ret;    
39437     },
39438     
39439     findBestRow: function(cells)
39440     {
39441         var ret = 0;
39442         
39443         for (var i =0 ; i < cells.length;i++) {
39444             ret  = Math.max(cells[i].rows || 0,ret);
39445         }
39446         return ret;
39447         
39448     },
39449     
39450     
39451     addItem : function(rec)
39452     {
39453         // look for vertical location slot in
39454         var cells = this.findCells(rec);
39455         
39456         rec.row = this.findBestRow(cells);
39457         
39458         // work out the location.
39459         
39460         var crow = false;
39461         var rows = [];
39462         for(var i =0; i < cells.length; i++) {
39463             if (!crow) {
39464                 crow = {
39465                     start : cells[i],
39466                     end :  cells[i]
39467                 };
39468                 continue;
39469             }
39470             if (crow.start.getY() == cells[i].getY()) {
39471                 // on same row.
39472                 crow.end = cells[i];
39473                 continue;
39474             }
39475             // different row.
39476             rows.push(crow);
39477             crow = {
39478                 start: cells[i],
39479                 end : cells[i]
39480             };
39481             
39482         }
39483         
39484         rows.push(crow);
39485         rec.els = [];
39486         rec.rows = rows;
39487         rec.cells = cells;
39488         for (var i = 0; i < cells.length;i++) {
39489             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
39490             
39491         }
39492         
39493         
39494     },
39495     
39496     clearEvents: function() {
39497         
39498         if (!this.eventStore.getCount()) {
39499             return;
39500         }
39501         // reset number of rows in cells.
39502         Roo.each(this.cells.elements, function(c){
39503             c.rows = 0;
39504         });
39505         
39506         this.eventStore.each(function(e) {
39507             this.clearEvent(e);
39508         },this);
39509         
39510     },
39511     
39512     clearEvent : function(ev)
39513     {
39514         if (ev.els) {
39515             Roo.each(ev.els, function(el) {
39516                 el.un('mouseenter' ,this.onEventEnter, this);
39517                 el.un('mouseleave' ,this.onEventLeave, this);
39518                 el.remove();
39519             },this);
39520             ev.els = [];
39521         }
39522     },
39523     
39524     
39525     renderEvent : function(ev,ctr) {
39526         if (!ctr) {
39527              ctr = this.view.el.select('.fc-event-container',true).first();
39528         }
39529         
39530          
39531         this.clearEvent(ev);
39532             //code
39533        
39534         
39535         
39536         ev.els = [];
39537         var cells = ev.cells;
39538         var rows = ev.rows;
39539         this.fireEvent('eventrender', this, ev);
39540         
39541         for(var i =0; i < rows.length; i++) {
39542             
39543             cls = '';
39544             if (i == 0) {
39545                 cls += ' fc-event-start';
39546             }
39547             if ((i+1) == rows.length) {
39548                 cls += ' fc-event-end';
39549             }
39550             
39551             //Roo.log(ev.data);
39552             // how many rows should it span..
39553             var cg = this.eventTmpl.append(ctr,Roo.apply({
39554                 fccls : cls
39555                 
39556             }, ev.data) , true);
39557             
39558             
39559             cg.on('mouseenter' ,this.onEventEnter, this, ev);
39560             cg.on('mouseleave' ,this.onEventLeave, this, ev);
39561             cg.on('click', this.onEventClick, this, ev);
39562             
39563             ev.els.push(cg);
39564             
39565             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
39566             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
39567             //Roo.log(cg);
39568              
39569             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
39570             cg.setWidth(ebox.right - sbox.x -2);
39571         }
39572     },
39573     
39574     renderEvents: function()
39575     {   
39576         // first make sure there is enough space..
39577         
39578         if (!this.eventTmpl) {
39579             this.eventTmpl = new Roo.Template(
39580                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
39581                     '<div class="fc-event-inner">' +
39582                         '<span class="fc-event-time">{time}</span>' +
39583                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
39584                     '</div>' +
39585                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
39586                 '</div>'
39587             );
39588                 
39589         }
39590                
39591         
39592         
39593         this.cells.each(function(c) {
39594             //Roo.log(c.select('.fc-day-content div',true).first());
39595             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
39596         });
39597         
39598         var ctr = this.view.el.select('.fc-event-container',true).first();
39599         
39600         var cls;
39601         this.eventStore.each(function(ev){
39602             
39603             this.renderEvent(ev);
39604              
39605              
39606         }, this);
39607         this.view.layout();
39608         
39609     },
39610     
39611     onEventEnter: function (e, el,event,d) {
39612         this.fireEvent('evententer', this, el, event);
39613     },
39614     
39615     onEventLeave: function (e, el,event,d) {
39616         this.fireEvent('eventleave', this, el, event);
39617     },
39618     
39619     onEventClick: function (e, el,event,d) {
39620         this.fireEvent('eventclick', this, el, event);
39621     },
39622     
39623     onMonthChange: function () {
39624         this.store.load();
39625     },
39626     
39627     onLoad: function () {
39628         
39629         //Roo.log('calendar onload');
39630 //         
39631         if(this.eventStore.getCount() > 0){
39632             
39633            
39634             
39635             this.eventStore.each(function(d){
39636                 
39637                 
39638                 // FIXME..
39639                 var add =   d.data;
39640                 if (typeof(add.end_dt) == 'undefined')  {
39641                     Roo.log("Missing End time in calendar data: ");
39642                     Roo.log(d);
39643                     return;
39644                 }
39645                 if (typeof(add.start_dt) == 'undefined')  {
39646                     Roo.log("Missing Start time in calendar data: ");
39647                     Roo.log(d);
39648                     return;
39649                 }
39650                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
39651                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
39652                 add.id = add.id || d.id;
39653                 add.title = add.title || '??';
39654                 
39655                 this.addItem(d);
39656                 
39657              
39658             },this);
39659         }
39660         
39661         this.renderEvents();
39662     }
39663     
39664
39665 });
39666 /*
39667  grid : {
39668                 xtype: 'Grid',
39669                 xns: Roo.grid,
39670                 listeners : {
39671                     render : function ()
39672                     {
39673                         _this.grid = this;
39674                         
39675                         if (!this.view.el.hasClass('course-timesheet')) {
39676                             this.view.el.addClass('course-timesheet');
39677                         }
39678                         if (this.tsStyle) {
39679                             this.ds.load({});
39680                             return; 
39681                         }
39682                         Roo.log('width');
39683                         Roo.log(_this.grid.view.el.getWidth());
39684                         
39685                         
39686                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
39687                             '.course-timesheet .x-grid-row' : {
39688                                 height: '80px'
39689                             },
39690                             '.x-grid-row td' : {
39691                                 'vertical-align' : 0
39692                             },
39693                             '.course-edit-link' : {
39694                                 'color' : 'blue',
39695                                 'text-overflow' : 'ellipsis',
39696                                 'overflow' : 'hidden',
39697                                 'white-space' : 'nowrap',
39698                                 'cursor' : 'pointer'
39699                             },
39700                             '.sub-link' : {
39701                                 'color' : 'green'
39702                             },
39703                             '.de-act-sup-link' : {
39704                                 'color' : 'purple',
39705                                 'text-decoration' : 'line-through'
39706                             },
39707                             '.de-act-link' : {
39708                                 'color' : 'red',
39709                                 'text-decoration' : 'line-through'
39710                             },
39711                             '.course-timesheet .course-highlight' : {
39712                                 'border-top-style': 'dashed !important',
39713                                 'border-bottom-bottom': 'dashed !important'
39714                             },
39715                             '.course-timesheet .course-item' : {
39716                                 'font-family'   : 'tahoma, arial, helvetica',
39717                                 'font-size'     : '11px',
39718                                 'overflow'      : 'hidden',
39719                                 'padding-left'  : '10px',
39720                                 'padding-right' : '10px',
39721                                 'padding-top' : '10px' 
39722                             }
39723                             
39724                         }, Roo.id());
39725                                 this.ds.load({});
39726                     }
39727                 },
39728                 autoWidth : true,
39729                 monitorWindowResize : false,
39730                 cellrenderer : function(v,x,r)
39731                 {
39732                     return v;
39733                 },
39734                 sm : {
39735                     xtype: 'CellSelectionModel',
39736                     xns: Roo.grid
39737                 },
39738                 dataSource : {
39739                     xtype: 'Store',
39740                     xns: Roo.data,
39741                     listeners : {
39742                         beforeload : function (_self, options)
39743                         {
39744                             options.params = options.params || {};
39745                             options.params._month = _this.monthField.getValue();
39746                             options.params.limit = 9999;
39747                             options.params['sort'] = 'when_dt';    
39748                             options.params['dir'] = 'ASC';    
39749                             this.proxy.loadResponse = this.loadResponse;
39750                             Roo.log("load?");
39751                             //this.addColumns();
39752                         },
39753                         load : function (_self, records, options)
39754                         {
39755                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
39756                                 // if you click on the translation.. you can edit it...
39757                                 var el = Roo.get(this);
39758                                 var id = el.dom.getAttribute('data-id');
39759                                 var d = el.dom.getAttribute('data-date');
39760                                 var t = el.dom.getAttribute('data-time');
39761                                 //var id = this.child('span').dom.textContent;
39762                                 
39763                                 //Roo.log(this);
39764                                 Pman.Dialog.CourseCalendar.show({
39765                                     id : id,
39766                                     when_d : d,
39767                                     when_t : t,
39768                                     productitem_active : id ? 1 : 0
39769                                 }, function() {
39770                                     _this.grid.ds.load({});
39771                                 });
39772                            
39773                            });
39774                            
39775                            _this.panel.fireEvent('resize', [ '', '' ]);
39776                         }
39777                     },
39778                     loadResponse : function(o, success, response){
39779                             // this is overridden on before load..
39780                             
39781                             Roo.log("our code?");       
39782                             //Roo.log(success);
39783                             //Roo.log(response)
39784                             delete this.activeRequest;
39785                             if(!success){
39786                                 this.fireEvent("loadexception", this, o, response);
39787                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
39788                                 return;
39789                             }
39790                             var result;
39791                             try {
39792                                 result = o.reader.read(response);
39793                             }catch(e){
39794                                 Roo.log("load exception?");
39795                                 this.fireEvent("loadexception", this, o, response, e);
39796                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
39797                                 return;
39798                             }
39799                             Roo.log("ready...");        
39800                             // loop through result.records;
39801                             // and set this.tdate[date] = [] << array of records..
39802                             _this.tdata  = {};
39803                             Roo.each(result.records, function(r){
39804                                 //Roo.log(r.data);
39805                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
39806                                     _this.tdata[r.data.when_dt.format('j')] = [];
39807                                 }
39808                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
39809                             });
39810                             
39811                             //Roo.log(_this.tdata);
39812                             
39813                             result.records = [];
39814                             result.totalRecords = 6;
39815                     
39816                             // let's generate some duumy records for the rows.
39817                             //var st = _this.dateField.getValue();
39818                             
39819                             // work out monday..
39820                             //st = st.add(Date.DAY, -1 * st.format('w'));
39821                             
39822                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39823                             
39824                             var firstOfMonth = date.getFirstDayOfMonth();
39825                             var days = date.getDaysInMonth();
39826                             var d = 1;
39827                             var firstAdded = false;
39828                             for (var i = 0; i < result.totalRecords ; i++) {
39829                                 //var d= st.add(Date.DAY, i);
39830                                 var row = {};
39831                                 var added = 0;
39832                                 for(var w = 0 ; w < 7 ; w++){
39833                                     if(!firstAdded && firstOfMonth != w){
39834                                         continue;
39835                                     }
39836                                     if(d > days){
39837                                         continue;
39838                                     }
39839                                     firstAdded = true;
39840                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
39841                                     row['weekday'+w] = String.format(
39842                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
39843                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
39844                                                     d,
39845                                                     date.format('Y-m-')+dd
39846                                                 );
39847                                     added++;
39848                                     if(typeof(_this.tdata[d]) != 'undefined'){
39849                                         Roo.each(_this.tdata[d], function(r){
39850                                             var is_sub = '';
39851                                             var deactive = '';
39852                                             var id = r.id;
39853                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
39854                                             if(r.parent_id*1>0){
39855                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
39856                                                 id = r.parent_id;
39857                                             }
39858                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
39859                                                 deactive = 'de-act-link';
39860                                             }
39861                                             
39862                                             row['weekday'+w] += String.format(
39863                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
39864                                                     id, //0
39865                                                     r.product_id_name, //1
39866                                                     r.when_dt.format('h:ia'), //2
39867                                                     is_sub, //3
39868                                                     deactive, //4
39869                                                     desc // 5
39870                                             );
39871                                         });
39872                                     }
39873                                     d++;
39874                                 }
39875                                 
39876                                 // only do this if something added..
39877                                 if(added > 0){ 
39878                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
39879                                 }
39880                                 
39881                                 
39882                                 // push it twice. (second one with an hour..
39883                                 
39884                             }
39885                             //Roo.log(result);
39886                             this.fireEvent("load", this, o, o.request.arg);
39887                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
39888                         },
39889                     sortInfo : {field: 'when_dt', direction : 'ASC' },
39890                     proxy : {
39891                         xtype: 'HttpProxy',
39892                         xns: Roo.data,
39893                         method : 'GET',
39894                         url : baseURL + '/Roo/Shop_course.php'
39895                     },
39896                     reader : {
39897                         xtype: 'JsonReader',
39898                         xns: Roo.data,
39899                         id : 'id',
39900                         fields : [
39901                             {
39902                                 'name': 'id',
39903                                 'type': 'int'
39904                             },
39905                             {
39906                                 'name': 'when_dt',
39907                                 'type': 'string'
39908                             },
39909                             {
39910                                 'name': 'end_dt',
39911                                 'type': 'string'
39912                             },
39913                             {
39914                                 'name': 'parent_id',
39915                                 'type': 'int'
39916                             },
39917                             {
39918                                 'name': 'product_id',
39919                                 'type': 'int'
39920                             },
39921                             {
39922                                 'name': 'productitem_id',
39923                                 'type': 'int'
39924                             },
39925                             {
39926                                 'name': 'guid',
39927                                 'type': 'int'
39928                             }
39929                         ]
39930                     }
39931                 },
39932                 toolbar : {
39933                     xtype: 'Toolbar',
39934                     xns: Roo,
39935                     items : [
39936                         {
39937                             xtype: 'Button',
39938                             xns: Roo.Toolbar,
39939                             listeners : {
39940                                 click : function (_self, e)
39941                                 {
39942                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39943                                     sd.setMonth(sd.getMonth()-1);
39944                                     _this.monthField.setValue(sd.format('Y-m-d'));
39945                                     _this.grid.ds.load({});
39946                                 }
39947                             },
39948                             text : "Back"
39949                         },
39950                         {
39951                             xtype: 'Separator',
39952                             xns: Roo.Toolbar
39953                         },
39954                         {
39955                             xtype: 'MonthField',
39956                             xns: Roo.form,
39957                             listeners : {
39958                                 render : function (_self)
39959                                 {
39960                                     _this.monthField = _self;
39961                                    // _this.monthField.set  today
39962                                 },
39963                                 select : function (combo, date)
39964                                 {
39965                                     _this.grid.ds.load({});
39966                                 }
39967                             },
39968                             value : (function() { return new Date(); })()
39969                         },
39970                         {
39971                             xtype: 'Separator',
39972                             xns: Roo.Toolbar
39973                         },
39974                         {
39975                             xtype: 'TextItem',
39976                             xns: Roo.Toolbar,
39977                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
39978                         },
39979                         {
39980                             xtype: 'Fill',
39981                             xns: Roo.Toolbar
39982                         },
39983                         {
39984                             xtype: 'Button',
39985                             xns: Roo.Toolbar,
39986                             listeners : {
39987                                 click : function (_self, e)
39988                                 {
39989                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39990                                     sd.setMonth(sd.getMonth()+1);
39991                                     _this.monthField.setValue(sd.format('Y-m-d'));
39992                                     _this.grid.ds.load({});
39993                                 }
39994                             },
39995                             text : "Next"
39996                         }
39997                     ]
39998                 },
39999                  
40000             }
40001         };
40002         
40003         *//*
40004  * Based on:
40005  * Ext JS Library 1.1.1
40006  * Copyright(c) 2006-2007, Ext JS, LLC.
40007  *
40008  * Originally Released Under LGPL - original licence link has changed is not relivant.
40009  *
40010  * Fork - LGPL
40011  * <script type="text/javascript">
40012  */
40013  
40014 /**
40015  * @class Roo.LoadMask
40016  * A simple utility class for generically masking elements while loading data.  If the element being masked has
40017  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40018  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
40019  * element's UpdateManager load indicator and will be destroyed after the initial load.
40020  * @constructor
40021  * Create a new LoadMask
40022  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40023  * @param {Object} config The config object
40024  */
40025 Roo.LoadMask = function(el, config){
40026     this.el = Roo.get(el);
40027     Roo.apply(this, config);
40028     if(this.store){
40029         this.store.on('beforeload', this.onBeforeLoad, this);
40030         this.store.on('load', this.onLoad, this);
40031         this.store.on('loadexception', this.onLoadException, this);
40032         this.removeMask = false;
40033     }else{
40034         var um = this.el.getUpdateManager();
40035         um.showLoadIndicator = false; // disable the default indicator
40036         um.on('beforeupdate', this.onBeforeLoad, this);
40037         um.on('update', this.onLoad, this);
40038         um.on('failure', this.onLoad, this);
40039         this.removeMask = true;
40040     }
40041 };
40042
40043 Roo.LoadMask.prototype = {
40044     /**
40045      * @cfg {Boolean} removeMask
40046      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40047      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
40048      */
40049     removeMask : false,
40050     /**
40051      * @cfg {String} msg
40052      * The text to display in a centered loading message box (defaults to 'Loading...')
40053      */
40054     msg : 'Loading...',
40055     /**
40056      * @cfg {String} msgCls
40057      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40058      */
40059     msgCls : 'x-mask-loading',
40060
40061     /**
40062      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40063      * @type Boolean
40064      */
40065     disabled: false,
40066
40067     /**
40068      * Disables the mask to prevent it from being displayed
40069      */
40070     disable : function(){
40071        this.disabled = true;
40072     },
40073
40074     /**
40075      * Enables the mask so that it can be displayed
40076      */
40077     enable : function(){
40078         this.disabled = false;
40079     },
40080     
40081     onLoadException : function()
40082     {
40083         Roo.log(arguments);
40084         
40085         if (typeof(arguments[3]) != 'undefined') {
40086             Roo.MessageBox.alert("Error loading",arguments[3]);
40087         } 
40088         /*
40089         try {
40090             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40091                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40092             }   
40093         } catch(e) {
40094             
40095         }
40096         */
40097     
40098         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
40099     },
40100     // private
40101     onLoad : function()
40102     {
40103         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
40104     },
40105
40106     // private
40107     onBeforeLoad : function(){
40108         if(!this.disabled){
40109             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
40110         }
40111     },
40112
40113     // private
40114     destroy : function(){
40115         if(this.store){
40116             this.store.un('beforeload', this.onBeforeLoad, this);
40117             this.store.un('load', this.onLoad, this);
40118             this.store.un('loadexception', this.onLoadException, this);
40119         }else{
40120             var um = this.el.getUpdateManager();
40121             um.un('beforeupdate', this.onBeforeLoad, this);
40122             um.un('update', this.onLoad, this);
40123             um.un('failure', this.onLoad, this);
40124         }
40125     }
40126 };/*
40127  * Based on:
40128  * Ext JS Library 1.1.1
40129  * Copyright(c) 2006-2007, Ext JS, LLC.
40130  *
40131  * Originally Released Under LGPL - original licence link has changed is not relivant.
40132  *
40133  * Fork - LGPL
40134  * <script type="text/javascript">
40135  */
40136
40137
40138 /**
40139  * @class Roo.XTemplate
40140  * @extends Roo.Template
40141  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40142 <pre><code>
40143 var t = new Roo.XTemplate(
40144         '&lt;select name="{name}"&gt;',
40145                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40146         '&lt;/select&gt;'
40147 );
40148  
40149 // then append, applying the master template values
40150  </code></pre>
40151  *
40152  * Supported features:
40153  *
40154  *  Tags:
40155
40156 <pre><code>
40157       {a_variable} - output encoded.
40158       {a_variable.format:("Y-m-d")} - call a method on the variable
40159       {a_variable:raw} - unencoded output
40160       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40161       {a_variable:this.method_on_template(...)} - call a method on the template object.
40162  
40163 </code></pre>
40164  *  The tpl tag:
40165 <pre><code>
40166         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40167         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40168         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40169         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40170   
40171         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40172         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40173 </code></pre>
40174  *      
40175  */
40176 Roo.XTemplate = function()
40177 {
40178     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40179     if (this.html) {
40180         this.compile();
40181     }
40182 };
40183
40184
40185 Roo.extend(Roo.XTemplate, Roo.Template, {
40186
40187     /**
40188      * The various sub templates
40189      */
40190     tpls : false,
40191     /**
40192      *
40193      * basic tag replacing syntax
40194      * WORD:WORD()
40195      *
40196      * // you can fake an object call by doing this
40197      *  x.t:(test,tesT) 
40198      * 
40199      */
40200     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40201
40202     /**
40203      * compile the template
40204      *
40205      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40206      *
40207      */
40208     compile: function()
40209     {
40210         var s = this.html;
40211      
40212         s = ['<tpl>', s, '</tpl>'].join('');
40213     
40214         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40215             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40216             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40217             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40218             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40219             m,
40220             id     = 0,
40221             tpls   = [];
40222     
40223         while(true == !!(m = s.match(re))){
40224             var forMatch   = m[0].match(nameRe),
40225                 ifMatch   = m[0].match(ifRe),
40226                 execMatch   = m[0].match(execRe),
40227                 namedMatch   = m[0].match(namedRe),
40228                 
40229                 exp  = null, 
40230                 fn   = null,
40231                 exec = null,
40232                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40233                 
40234             if (ifMatch) {
40235                 // if - puts fn into test..
40236                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40237                 if(exp){
40238                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40239                 }
40240             }
40241             
40242             if (execMatch) {
40243                 // exec - calls a function... returns empty if true is  returned.
40244                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40245                 if(exp){
40246                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40247                 }
40248             }
40249             
40250             
40251             if (name) {
40252                 // for = 
40253                 switch(name){
40254                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40255                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40256                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40257                 }
40258             }
40259             var uid = namedMatch ? namedMatch[1] : id;
40260             
40261             
40262             tpls.push({
40263                 id:     namedMatch ? namedMatch[1] : id,
40264                 target: name,
40265                 exec:   exec,
40266                 test:   fn,
40267                 body:   m[1] || ''
40268             });
40269             if (namedMatch) {
40270                 s = s.replace(m[0], '');
40271             } else { 
40272                 s = s.replace(m[0], '{xtpl'+ id + '}');
40273             }
40274             ++id;
40275         }
40276         this.tpls = [];
40277         for(var i = tpls.length-1; i >= 0; --i){
40278             this.compileTpl(tpls[i]);
40279             this.tpls[tpls[i].id] = tpls[i];
40280         }
40281         this.master = tpls[tpls.length-1];
40282         return this;
40283     },
40284     /**
40285      * same as applyTemplate, except it's done to one of the subTemplates
40286      * when using named templates, you can do:
40287      *
40288      * var str = pl.applySubTemplate('your-name', values);
40289      *
40290      * 
40291      * @param {Number} id of the template
40292      * @param {Object} values to apply to template
40293      * @param {Object} parent (normaly the instance of this object)
40294      */
40295     applySubTemplate : function(id, values, parent)
40296     {
40297         
40298         
40299         var t = this.tpls[id];
40300         
40301         
40302         try { 
40303             if(t.test && !t.test.call(this, values, parent)){
40304                 return '';
40305             }
40306         } catch(e) {
40307             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40308             Roo.log(e.toString());
40309             Roo.log(t.test);
40310             return ''
40311         }
40312         try { 
40313             
40314             if(t.exec && t.exec.call(this, values, parent)){
40315                 return '';
40316             }
40317         } catch(e) {
40318             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40319             Roo.log(e.toString());
40320             Roo.log(t.exec);
40321             return ''
40322         }
40323         try {
40324             var vs = t.target ? t.target.call(this, values, parent) : values;
40325             parent = t.target ? values : parent;
40326             if(t.target && vs instanceof Array){
40327                 var buf = [];
40328                 for(var i = 0, len = vs.length; i < len; i++){
40329                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40330                 }
40331                 return buf.join('');
40332             }
40333             return t.compiled.call(this, vs, parent);
40334         } catch (e) {
40335             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40336             Roo.log(e.toString());
40337             Roo.log(t.compiled);
40338             return '';
40339         }
40340     },
40341
40342     compileTpl : function(tpl)
40343     {
40344         var fm = Roo.util.Format;
40345         var useF = this.disableFormats !== true;
40346         var sep = Roo.isGecko ? "+" : ",";
40347         var undef = function(str) {
40348             Roo.log("Property not found :"  + str);
40349             return '';
40350         };
40351         
40352         var fn = function(m, name, format, args)
40353         {
40354             //Roo.log(arguments);
40355             args = args ? args.replace(/\\'/g,"'") : args;
40356             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40357             if (typeof(format) == 'undefined') {
40358                 format= 'htmlEncode';
40359             }
40360             if (format == 'raw' ) {
40361                 format = false;
40362             }
40363             
40364             if(name.substr(0, 4) == 'xtpl'){
40365                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40366             }
40367             
40368             // build an array of options to determine if value is undefined..
40369             
40370             // basically get 'xxxx.yyyy' then do
40371             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40372             //    (function () { Roo.log("Property not found"); return ''; })() :
40373             //    ......
40374             
40375             var udef_ar = [];
40376             var lookfor = '';
40377             Roo.each(name.split('.'), function(st) {
40378                 lookfor += (lookfor.length ? '.': '') + st;
40379                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40380             });
40381             
40382             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40383             
40384             
40385             if(format && useF){
40386                 
40387                 args = args ? ',' + args : "";
40388                  
40389                 if(format.substr(0, 5) != "this."){
40390                     format = "fm." + format + '(';
40391                 }else{
40392                     format = 'this.call("'+ format.substr(5) + '", ';
40393                     args = ", values";
40394                 }
40395                 
40396                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40397             }
40398              
40399             if (args.length) {
40400                 // called with xxyx.yuu:(test,test)
40401                 // change to ()
40402                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40403             }
40404             // raw.. - :raw modifier..
40405             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40406             
40407         };
40408         var body;
40409         // branched to use + in gecko and [].join() in others
40410         if(Roo.isGecko){
40411             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40412                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40413                     "';};};";
40414         }else{
40415             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40416             body.push(tpl.body.replace(/(\r\n|\n)/g,
40417                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40418             body.push("'].join('');};};");
40419             body = body.join('');
40420         }
40421         
40422         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40423        
40424         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40425         eval(body);
40426         
40427         return this;
40428     },
40429
40430     applyTemplate : function(values){
40431         return this.master.compiled.call(this, values, {});
40432         //var s = this.subs;
40433     },
40434
40435     apply : function(){
40436         return this.applyTemplate.apply(this, arguments);
40437     }
40438
40439  });
40440
40441 Roo.XTemplate.from = function(el){
40442     el = Roo.getDom(el);
40443     return new Roo.XTemplate(el.value || el.innerHTML);
40444 };