sync
[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  * @static
10306  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10307  * Example usage:
10308  *<pre><code>
10309 // Basic alert:
10310 Roo.Msg.alert('Status', 'Changes saved successfully.');
10311
10312 // Prompt for user data:
10313 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10314     if (btn == 'ok'){
10315         // process text value...
10316     }
10317 });
10318
10319 // Show a dialog using config options:
10320 Roo.Msg.show({
10321    title:'Save Changes?',
10322    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10323    buttons: Roo.Msg.YESNOCANCEL,
10324    fn: processResult,
10325    animEl: 'elId'
10326 });
10327 </code></pre>
10328  * @static
10329  */
10330 Roo.MessageBox = function(){
10331     var dlg, opt, mask, waitTimer;
10332     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10333     var buttons, activeTextEl, bwidth;
10334
10335     // private
10336     var handleButton = function(button){
10337         dlg.hide();
10338         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10339     };
10340
10341     // private
10342     var handleHide = function(){
10343         if(opt && opt.cls){
10344             dlg.el.removeClass(opt.cls);
10345         }
10346         if(waitTimer){
10347             Roo.TaskMgr.stop(waitTimer);
10348             waitTimer = null;
10349         }
10350     };
10351
10352     // private
10353     var updateButtons = function(b){
10354         var width = 0;
10355         if(!b){
10356             buttons["ok"].hide();
10357             buttons["cancel"].hide();
10358             buttons["yes"].hide();
10359             buttons["no"].hide();
10360             dlg.footer.dom.style.display = 'none';
10361             return width;
10362         }
10363         dlg.footer.dom.style.display = '';
10364         for(var k in buttons){
10365             if(typeof buttons[k] != "function"){
10366                 if(b[k]){
10367                     buttons[k].show();
10368                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10369                     width += buttons[k].el.getWidth()+15;
10370                 }else{
10371                     buttons[k].hide();
10372                 }
10373             }
10374         }
10375         return width;
10376     };
10377
10378     // private
10379     var handleEsc = function(d, k, e){
10380         if(opt && opt.closable !== false){
10381             dlg.hide();
10382         }
10383         if(e){
10384             e.stopEvent();
10385         }
10386     };
10387
10388     return {
10389         /**
10390          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10391          * @return {Roo.BasicDialog} The BasicDialog element
10392          */
10393         getDialog : function(){
10394            if(!dlg){
10395                 dlg = new Roo.BasicDialog("x-msg-box", {
10396                     autoCreate : true,
10397                     shadow: true,
10398                     draggable: true,
10399                     resizable:false,
10400                     constraintoviewport:false,
10401                     fixedcenter:true,
10402                     collapsible : false,
10403                     shim:true,
10404                     modal: true,
10405                     width:400, height:100,
10406                     buttonAlign:"center",
10407                     closeClick : function(){
10408                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10409                             handleButton("no");
10410                         }else{
10411                             handleButton("cancel");
10412                         }
10413                     }
10414                 });
10415                 dlg.on("hide", handleHide);
10416                 mask = dlg.mask;
10417                 dlg.addKeyListener(27, handleEsc);
10418                 buttons = {};
10419                 var bt = this.buttonText;
10420                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10421                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10422                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10423                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10424                 bodyEl = dlg.body.createChild({
10425
10426                     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>'
10427                 });
10428                 msgEl = bodyEl.dom.firstChild;
10429                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10430                 textboxEl.enableDisplayMode();
10431                 textboxEl.addKeyListener([10,13], function(){
10432                     if(dlg.isVisible() && opt && opt.buttons){
10433                         if(opt.buttons.ok){
10434                             handleButton("ok");
10435                         }else if(opt.buttons.yes){
10436                             handleButton("yes");
10437                         }
10438                     }
10439                 });
10440                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10441                 textareaEl.enableDisplayMode();
10442                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10443                 progressEl.enableDisplayMode();
10444                 var pf = progressEl.dom.firstChild;
10445                 if (pf) {
10446                     pp = Roo.get(pf.firstChild);
10447                     pp.setHeight(pf.offsetHeight);
10448                 }
10449                 
10450             }
10451             return dlg;
10452         },
10453
10454         /**
10455          * Updates the message box body text
10456          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10457          * the XHTML-compliant non-breaking space character '&amp;#160;')
10458          * @return {Roo.MessageBox} This message box
10459          */
10460         updateText : function(text){
10461             if(!dlg.isVisible() && !opt.width){
10462                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10463             }
10464             msgEl.innerHTML = text || '&#160;';
10465       
10466             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10467             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10468             var w = Math.max(
10469                     Math.min(opt.width || cw , this.maxWidth), 
10470                     Math.max(opt.minWidth || this.minWidth, bwidth)
10471             );
10472             if(opt.prompt){
10473                 activeTextEl.setWidth(w);
10474             }
10475             if(dlg.isVisible()){
10476                 dlg.fixedcenter = false;
10477             }
10478             // to big, make it scroll. = But as usual stupid IE does not support
10479             // !important..
10480             
10481             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10482                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10483                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10484             } else {
10485                 bodyEl.dom.style.height = '';
10486                 bodyEl.dom.style.overflowY = '';
10487             }
10488             if (cw > w) {
10489                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10490             } else {
10491                 bodyEl.dom.style.overflowX = '';
10492             }
10493             
10494             dlg.setContentSize(w, bodyEl.getHeight());
10495             if(dlg.isVisible()){
10496                 dlg.fixedcenter = true;
10497             }
10498             return this;
10499         },
10500
10501         /**
10502          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10503          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10504          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10505          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10506          * @return {Roo.MessageBox} This message box
10507          */
10508         updateProgress : function(value, text){
10509             if(text){
10510                 this.updateText(text);
10511             }
10512             if (pp) { // weird bug on my firefox - for some reason this is not defined
10513                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10514             }
10515             return this;
10516         },        
10517
10518         /**
10519          * Returns true if the message box is currently displayed
10520          * @return {Boolean} True if the message box is visible, else false
10521          */
10522         isVisible : function(){
10523             return dlg && dlg.isVisible();  
10524         },
10525
10526         /**
10527          * Hides the message box if it is displayed
10528          */
10529         hide : function(){
10530             if(this.isVisible()){
10531                 dlg.hide();
10532             }  
10533         },
10534
10535         /**
10536          * Displays a new message box, or reinitializes an existing message box, based on the config options
10537          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10538          * The following config object properties are supported:
10539          * <pre>
10540 Property    Type             Description
10541 ----------  ---------------  ------------------------------------------------------------------------------------
10542 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10543                                    closes (defaults to undefined)
10544 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10545                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10546 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10547                                    progress and wait dialogs will ignore this property and always hide the
10548                                    close button as they can only be closed programmatically.
10549 cls               String           A custom CSS class to apply to the message box element
10550 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10551                                    displayed (defaults to 75)
10552 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10553                                    function will be btn (the name of the button that was clicked, if applicable,
10554                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10555                                    Progress and wait dialogs will ignore this option since they do not respond to
10556                                    user actions and can only be closed programmatically, so any required function
10557                                    should be called by the same code after it closes the dialog.
10558 icon              String           A CSS class that provides a background image to be used as an icon for
10559                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10560 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10561 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10562 modal             Boolean          False to allow user interaction with the page while the message box is
10563                                    displayed (defaults to true)
10564 msg               String           A string that will replace the existing message box body text (defaults
10565                                    to the XHTML-compliant non-breaking space character '&#160;')
10566 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10567 progress          Boolean          True to display a progress bar (defaults to false)
10568 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10569 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10570 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10571 title             String           The title text
10572 value             String           The string value to set into the active textbox element if displayed
10573 wait              Boolean          True to display a progress bar (defaults to false)
10574 width             Number           The width of the dialog in pixels
10575 </pre>
10576          *
10577          * Example usage:
10578          * <pre><code>
10579 Roo.Msg.show({
10580    title: 'Address',
10581    msg: 'Please enter your address:',
10582    width: 300,
10583    buttons: Roo.MessageBox.OKCANCEL,
10584    multiline: true,
10585    fn: saveAddress,
10586    animEl: 'addAddressBtn'
10587 });
10588 </code></pre>
10589          * @param {Object} config Configuration options
10590          * @return {Roo.MessageBox} This message box
10591          */
10592         show : function(options)
10593         {
10594             
10595             // this causes nightmares if you show one dialog after another
10596             // especially on callbacks..
10597              
10598             if(this.isVisible()){
10599                 
10600                 this.hide();
10601                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10602                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10603                 Roo.log("New Dialog Message:" +  options.msg )
10604                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10605                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10606                 
10607             }
10608             var d = this.getDialog();
10609             opt = options;
10610             d.setTitle(opt.title || "&#160;");
10611             d.close.setDisplayed(opt.closable !== false);
10612             activeTextEl = textboxEl;
10613             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10614             if(opt.prompt){
10615                 if(opt.multiline){
10616                     textboxEl.hide();
10617                     textareaEl.show();
10618                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10619                         opt.multiline : this.defaultTextHeight);
10620                     activeTextEl = textareaEl;
10621                 }else{
10622                     textboxEl.show();
10623                     textareaEl.hide();
10624                 }
10625             }else{
10626                 textboxEl.hide();
10627                 textareaEl.hide();
10628             }
10629             progressEl.setDisplayed(opt.progress === true);
10630             this.updateProgress(0);
10631             activeTextEl.dom.value = opt.value || "";
10632             if(opt.prompt){
10633                 dlg.setDefaultButton(activeTextEl);
10634             }else{
10635                 var bs = opt.buttons;
10636                 var db = null;
10637                 if(bs && bs.ok){
10638                     db = buttons["ok"];
10639                 }else if(bs && bs.yes){
10640                     db = buttons["yes"];
10641                 }
10642                 dlg.setDefaultButton(db);
10643             }
10644             bwidth = updateButtons(opt.buttons);
10645             this.updateText(opt.msg);
10646             if(opt.cls){
10647                 d.el.addClass(opt.cls);
10648             }
10649             d.proxyDrag = opt.proxyDrag === true;
10650             d.modal = opt.modal !== false;
10651             d.mask = opt.modal !== false ? mask : false;
10652             if(!d.isVisible()){
10653                 // force it to the end of the z-index stack so it gets a cursor in FF
10654                 document.body.appendChild(dlg.el.dom);
10655                 d.animateTarget = null;
10656                 d.show(options.animEl);
10657             }
10658             return this;
10659         },
10660
10661         /**
10662          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10663          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10664          * and closing the message box when the process is complete.
10665          * @param {String} title The title bar text
10666          * @param {String} msg The message box body text
10667          * @return {Roo.MessageBox} This message box
10668          */
10669         progress : function(title, msg){
10670             this.show({
10671                 title : title,
10672                 msg : msg,
10673                 buttons: false,
10674                 progress:true,
10675                 closable:false,
10676                 minWidth: this.minProgressWidth,
10677                 modal : true
10678             });
10679             return this;
10680         },
10681
10682         /**
10683          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10684          * If a callback function is passed it will be called after the user clicks the button, and the
10685          * id of the button that was clicked will be passed as the only parameter to the callback
10686          * (could also be the top-right close button).
10687          * @param {String} title The title bar text
10688          * @param {String} msg The message box body text
10689          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10690          * @param {Object} scope (optional) The scope of the callback function
10691          * @return {Roo.MessageBox} This message box
10692          */
10693         alert : function(title, msg, fn, scope){
10694             this.show({
10695                 title : title,
10696                 msg : msg,
10697                 buttons: this.OK,
10698                 fn: fn,
10699                 scope : scope,
10700                 modal : true
10701             });
10702             return this;
10703         },
10704
10705         /**
10706          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10707          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10708          * You are responsible for closing the message box when the process is complete.
10709          * @param {String} msg The message box body text
10710          * @param {String} title (optional) The title bar text
10711          * @return {Roo.MessageBox} This message box
10712          */
10713         wait : function(msg, title){
10714             this.show({
10715                 title : title,
10716                 msg : msg,
10717                 buttons: false,
10718                 closable:false,
10719                 progress:true,
10720                 modal:true,
10721                 width:300,
10722                 wait:true
10723             });
10724             waitTimer = Roo.TaskMgr.start({
10725                 run: function(i){
10726                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10727                 },
10728                 interval: 1000
10729             });
10730             return this;
10731         },
10732
10733         /**
10734          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10735          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10736          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10737          * @param {String} title The title bar text
10738          * @param {String} msg The message box body text
10739          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10740          * @param {Object} scope (optional) The scope of the callback function
10741          * @return {Roo.MessageBox} This message box
10742          */
10743         confirm : function(title, msg, fn, scope){
10744             this.show({
10745                 title : title,
10746                 msg : msg,
10747                 buttons: this.YESNO,
10748                 fn: fn,
10749                 scope : scope,
10750                 modal : true
10751             });
10752             return this;
10753         },
10754
10755         /**
10756          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10757          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10758          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10759          * (could also be the top-right close button) and the text that was entered will be passed as the two
10760          * parameters to the callback.
10761          * @param {String} title The title bar text
10762          * @param {String} msg The message box body text
10763          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10764          * @param {Object} scope (optional) The scope of the callback function
10765          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10766          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10767          * @return {Roo.MessageBox} This message box
10768          */
10769         prompt : function(title, msg, fn, scope, multiline){
10770             this.show({
10771                 title : title,
10772                 msg : msg,
10773                 buttons: this.OKCANCEL,
10774                 fn: fn,
10775                 minWidth:250,
10776                 scope : scope,
10777                 prompt:true,
10778                 multiline: multiline,
10779                 modal : true
10780             });
10781             return this;
10782         },
10783
10784         /**
10785          * Button config that displays a single OK button
10786          * @type Object
10787          */
10788         OK : {ok:true},
10789         /**
10790          * Button config that displays Yes and No buttons
10791          * @type Object
10792          */
10793         YESNO : {yes:true, no:true},
10794         /**
10795          * Button config that displays OK and Cancel buttons
10796          * @type Object
10797          */
10798         OKCANCEL : {ok:true, cancel:true},
10799         /**
10800          * Button config that displays Yes, No and Cancel buttons
10801          * @type Object
10802          */
10803         YESNOCANCEL : {yes:true, no:true, cancel:true},
10804
10805         /**
10806          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10807          * @type Number
10808          */
10809         defaultTextHeight : 75,
10810         /**
10811          * The maximum width in pixels of the message box (defaults to 600)
10812          * @type Number
10813          */
10814         maxWidth : 600,
10815         /**
10816          * The minimum width in pixels of the message box (defaults to 100)
10817          * @type Number
10818          */
10819         minWidth : 100,
10820         /**
10821          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10822          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10823          * @type Number
10824          */
10825         minProgressWidth : 250,
10826         /**
10827          * An object containing the default button text strings that can be overriden for localized language support.
10828          * Supported properties are: ok, cancel, yes and no.
10829          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10830          * @type Object
10831          */
10832         buttonText : {
10833             ok : "OK",
10834             cancel : "Cancel",
10835             yes : "Yes",
10836             no : "No"
10837         }
10838     };
10839 }();
10840
10841 /**
10842  * Shorthand for {@link Roo.MessageBox}
10843  */
10844 Roo.Msg = Roo.MessageBox;/*
10845  * Based on:
10846  * Ext JS Library 1.1.1
10847  * Copyright(c) 2006-2007, Ext JS, LLC.
10848  *
10849  * Originally Released Under LGPL - original licence link has changed is not relivant.
10850  *
10851  * Fork - LGPL
10852  * <script type="text/javascript">
10853  */
10854 /**
10855  * @class Roo.QuickTips
10856  * Provides attractive and customizable tooltips for any element.
10857  * @static
10858  */
10859 Roo.QuickTips = function(){
10860     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10861     var ce, bd, xy, dd;
10862     var visible = false, disabled = true, inited = false;
10863     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10864     
10865     var onOver = function(e){
10866         if(disabled){
10867             return;
10868         }
10869         var t = e.getTarget();
10870         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10871             return;
10872         }
10873         if(ce && t == ce.el){
10874             clearTimeout(hideProc);
10875             return;
10876         }
10877         if(t && tagEls[t.id]){
10878             tagEls[t.id].el = t;
10879             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10880             return;
10881         }
10882         var ttp, et = Roo.fly(t);
10883         var ns = cfg.namespace;
10884         if(tm.interceptTitles && t.title){
10885             ttp = t.title;
10886             t.qtip = ttp;
10887             t.removeAttribute("title");
10888             e.preventDefault();
10889         }else{
10890             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10891         }
10892         if(ttp){
10893             showProc = show.defer(tm.showDelay, tm, [{
10894                 el: t, 
10895                 text: ttp.replace(/\\n/g,'<br/>'),
10896                 width: et.getAttributeNS(ns, cfg.width),
10897                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10898                 title: et.getAttributeNS(ns, cfg.title),
10899                     cls: et.getAttributeNS(ns, cfg.cls)
10900             }]);
10901         }
10902     };
10903     
10904     var onOut = function(e){
10905         clearTimeout(showProc);
10906         var t = e.getTarget();
10907         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10908             hideProc = setTimeout(hide, tm.hideDelay);
10909         }
10910     };
10911     
10912     var onMove = function(e){
10913         if(disabled){
10914             return;
10915         }
10916         xy = e.getXY();
10917         xy[1] += 18;
10918         if(tm.trackMouse && ce){
10919             el.setXY(xy);
10920         }
10921     };
10922     
10923     var onDown = function(e){
10924         clearTimeout(showProc);
10925         clearTimeout(hideProc);
10926         if(!e.within(el)){
10927             if(tm.hideOnClick){
10928                 hide();
10929                 tm.disable();
10930                 tm.enable.defer(100, tm);
10931             }
10932         }
10933     };
10934     
10935     var getPad = function(){
10936         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10937     };
10938
10939     var show = function(o){
10940         if(disabled){
10941             return;
10942         }
10943         clearTimeout(dismissProc);
10944         ce = o;
10945         if(removeCls){ // in case manually hidden
10946             el.removeClass(removeCls);
10947             removeCls = null;
10948         }
10949         if(ce.cls){
10950             el.addClass(ce.cls);
10951             removeCls = ce.cls;
10952         }
10953         if(ce.title){
10954             tipTitle.update(ce.title);
10955             tipTitle.show();
10956         }else{
10957             tipTitle.update('');
10958             tipTitle.hide();
10959         }
10960         el.dom.style.width  = tm.maxWidth+'px';
10961         //tipBody.dom.style.width = '';
10962         tipBodyText.update(o.text);
10963         var p = getPad(), w = ce.width;
10964         if(!w){
10965             var td = tipBodyText.dom;
10966             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10967             if(aw > tm.maxWidth){
10968                 w = tm.maxWidth;
10969             }else if(aw < tm.minWidth){
10970                 w = tm.minWidth;
10971             }else{
10972                 w = aw;
10973             }
10974         }
10975         //tipBody.setWidth(w);
10976         el.setWidth(parseInt(w, 10) + p);
10977         if(ce.autoHide === false){
10978             close.setDisplayed(true);
10979             if(dd){
10980                 dd.unlock();
10981             }
10982         }else{
10983             close.setDisplayed(false);
10984             if(dd){
10985                 dd.lock();
10986             }
10987         }
10988         if(xy){
10989             el.avoidY = xy[1]-18;
10990             el.setXY(xy);
10991         }
10992         if(tm.animate){
10993             el.setOpacity(.1);
10994             el.setStyle("visibility", "visible");
10995             el.fadeIn({callback: afterShow});
10996         }else{
10997             afterShow();
10998         }
10999     };
11000     
11001     var afterShow = function(){
11002         if(ce){
11003             el.show();
11004             esc.enable();
11005             if(tm.autoDismiss && ce.autoHide !== false){
11006                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11007             }
11008         }
11009     };
11010     
11011     var hide = function(noanim){
11012         clearTimeout(dismissProc);
11013         clearTimeout(hideProc);
11014         ce = null;
11015         if(el.isVisible()){
11016             esc.disable();
11017             if(noanim !== true && tm.animate){
11018                 el.fadeOut({callback: afterHide});
11019             }else{
11020                 afterHide();
11021             } 
11022         }
11023     };
11024     
11025     var afterHide = function(){
11026         el.hide();
11027         if(removeCls){
11028             el.removeClass(removeCls);
11029             removeCls = null;
11030         }
11031     };
11032     
11033     return {
11034         /**
11035         * @cfg {Number} minWidth
11036         * The minimum width of the quick tip (defaults to 40)
11037         */
11038        minWidth : 40,
11039         /**
11040         * @cfg {Number} maxWidth
11041         * The maximum width of the quick tip (defaults to 300)
11042         */
11043        maxWidth : 300,
11044         /**
11045         * @cfg {Boolean} interceptTitles
11046         * True to automatically use the element's DOM title value if available (defaults to false)
11047         */
11048        interceptTitles : false,
11049         /**
11050         * @cfg {Boolean} trackMouse
11051         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11052         */
11053        trackMouse : false,
11054         /**
11055         * @cfg {Boolean} hideOnClick
11056         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11057         */
11058        hideOnClick : true,
11059         /**
11060         * @cfg {Number} showDelay
11061         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11062         */
11063        showDelay : 500,
11064         /**
11065         * @cfg {Number} hideDelay
11066         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11067         */
11068        hideDelay : 200,
11069         /**
11070         * @cfg {Boolean} autoHide
11071         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11072         * Used in conjunction with hideDelay.
11073         */
11074        autoHide : true,
11075         /**
11076         * @cfg {Boolean}
11077         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11078         * (defaults to true).  Used in conjunction with autoDismissDelay.
11079         */
11080        autoDismiss : true,
11081         /**
11082         * @cfg {Number}
11083         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11084         */
11085        autoDismissDelay : 5000,
11086        /**
11087         * @cfg {Boolean} animate
11088         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11089         */
11090        animate : false,
11091
11092        /**
11093         * @cfg {String} title
11094         * Title text to display (defaults to '').  This can be any valid HTML markup.
11095         */
11096         title: '',
11097        /**
11098         * @cfg {String} text
11099         * Body text to display (defaults to '').  This can be any valid HTML markup.
11100         */
11101         text : '',
11102        /**
11103         * @cfg {String} cls
11104         * A CSS class to apply to the base quick tip element (defaults to '').
11105         */
11106         cls : '',
11107        /**
11108         * @cfg {Number} width
11109         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11110         * minWidth or maxWidth.
11111         */
11112         width : null,
11113
11114     /**
11115      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11116      * or display QuickTips in a page.
11117      */
11118        init : function(){
11119           tm = Roo.QuickTips;
11120           cfg = tm.tagConfig;
11121           if(!inited){
11122               if(!Roo.isReady){ // allow calling of init() before onReady
11123                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11124                   return;
11125               }
11126               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11127               el.fxDefaults = {stopFx: true};
11128               // maximum custom styling
11129               //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>');
11130               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>');              
11131               tipTitle = el.child('h3');
11132               tipTitle.enableDisplayMode("block");
11133               tipBody = el.child('div.x-tip-bd');
11134               tipBodyText = el.child('div.x-tip-bd-inner');
11135               //bdLeft = el.child('div.x-tip-bd-left');
11136               //bdRight = el.child('div.x-tip-bd-right');
11137               close = el.child('div.x-tip-close');
11138               close.enableDisplayMode("block");
11139               close.on("click", hide);
11140               var d = Roo.get(document);
11141               d.on("mousedown", onDown);
11142               d.on("mouseover", onOver);
11143               d.on("mouseout", onOut);
11144               d.on("mousemove", onMove);
11145               esc = d.addKeyListener(27, hide);
11146               esc.disable();
11147               if(Roo.dd.DD){
11148                   dd = el.initDD("default", null, {
11149                       onDrag : function(){
11150                           el.sync();  
11151                       }
11152                   });
11153                   dd.setHandleElId(tipTitle.id);
11154                   dd.lock();
11155               }
11156               inited = true;
11157           }
11158           this.enable(); 
11159        },
11160
11161     /**
11162      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11163      * are supported:
11164      * <pre>
11165 Property    Type                   Description
11166 ----------  ---------------------  ------------------------------------------------------------------------
11167 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11168      * </ul>
11169      * @param {Object} config The config object
11170      */
11171        register : function(config){
11172            var cs = config instanceof Array ? config : arguments;
11173            for(var i = 0, len = cs.length; i < len; i++) {
11174                var c = cs[i];
11175                var target = c.target;
11176                if(target){
11177                    if(target instanceof Array){
11178                        for(var j = 0, jlen = target.length; j < jlen; j++){
11179                            tagEls[target[j]] = c;
11180                        }
11181                    }else{
11182                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11183                    }
11184                }
11185            }
11186        },
11187
11188     /**
11189      * Removes this quick tip from its element and destroys it.
11190      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11191      */
11192        unregister : function(el){
11193            delete tagEls[Roo.id(el)];
11194        },
11195
11196     /**
11197      * Enable this quick tip.
11198      */
11199        enable : function(){
11200            if(inited && disabled){
11201                locks.pop();
11202                if(locks.length < 1){
11203                    disabled = false;
11204                }
11205            }
11206        },
11207
11208     /**
11209      * Disable this quick tip.
11210      */
11211        disable : function(){
11212           disabled = true;
11213           clearTimeout(showProc);
11214           clearTimeout(hideProc);
11215           clearTimeout(dismissProc);
11216           if(ce){
11217               hide(true);
11218           }
11219           locks.push(1);
11220        },
11221
11222     /**
11223      * Returns true if the quick tip is enabled, else false.
11224      */
11225        isEnabled : function(){
11226             return !disabled;
11227        },
11228
11229         // private
11230        tagConfig : {
11231            namespace : "roo", // was ext?? this may break..
11232            alt_namespace : "ext",
11233            attribute : "qtip",
11234            width : "width",
11235            target : "target",
11236            title : "qtitle",
11237            hide : "hide",
11238            cls : "qclass"
11239        }
11240    };
11241 }();
11242
11243 // backwards compat
11244 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11245  * Based on:
11246  * Ext JS Library 1.1.1
11247  * Copyright(c) 2006-2007, Ext JS, LLC.
11248  *
11249  * Originally Released Under LGPL - original licence link has changed is not relivant.
11250  *
11251  * Fork - LGPL
11252  * <script type="text/javascript">
11253  */
11254  
11255
11256 /**
11257  * @class Roo.tree.TreePanel
11258  * @extends Roo.data.Tree
11259  * @cfg {Roo.tree.TreeNode} root The root node
11260  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11261  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11262  * @cfg {Boolean} enableDD true to enable drag and drop
11263  * @cfg {Boolean} enableDrag true to enable just drag
11264  * @cfg {Boolean} enableDrop true to enable just drop
11265  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11266  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11267  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11268  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11269  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11270  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11271  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11272  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11273  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11274  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11275  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11276  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11277  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11278  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11279  * @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>
11280  * @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>
11281  * 
11282  * @constructor
11283  * @param {String/HTMLElement/Element} el The container element
11284  * @param {Object} config
11285  */
11286 Roo.tree.TreePanel = function(el, config){
11287     var root = false;
11288     var loader = false;
11289     if (config.root) {
11290         root = config.root;
11291         delete config.root;
11292     }
11293     if (config.loader) {
11294         loader = config.loader;
11295         delete config.loader;
11296     }
11297     
11298     Roo.apply(this, config);
11299     Roo.tree.TreePanel.superclass.constructor.call(this);
11300     this.el = Roo.get(el);
11301     this.el.addClass('x-tree');
11302     //console.log(root);
11303     if (root) {
11304         this.setRootNode( Roo.factory(root, Roo.tree));
11305     }
11306     if (loader) {
11307         this.loader = Roo.factory(loader, Roo.tree);
11308     }
11309    /**
11310     * Read-only. The id of the container element becomes this TreePanel's id.
11311     */
11312     this.id = this.el.id;
11313     this.addEvents({
11314         /**
11315         * @event beforeload
11316         * Fires before a node is loaded, return false to cancel
11317         * @param {Node} node The node being loaded
11318         */
11319         "beforeload" : true,
11320         /**
11321         * @event load
11322         * Fires when a node is loaded
11323         * @param {Node} node The node that was loaded
11324         */
11325         "load" : true,
11326         /**
11327         * @event textchange
11328         * Fires when the text for a node is changed
11329         * @param {Node} node The node
11330         * @param {String} text The new text
11331         * @param {String} oldText The old text
11332         */
11333         "textchange" : true,
11334         /**
11335         * @event beforeexpand
11336         * Fires before a node is expanded, return false to cancel.
11337         * @param {Node} node The node
11338         * @param {Boolean} deep
11339         * @param {Boolean} anim
11340         */
11341         "beforeexpand" : true,
11342         /**
11343         * @event beforecollapse
11344         * Fires before a node is collapsed, return false to cancel.
11345         * @param {Node} node The node
11346         * @param {Boolean} deep
11347         * @param {Boolean} anim
11348         */
11349         "beforecollapse" : true,
11350         /**
11351         * @event expand
11352         * Fires when a node is expanded
11353         * @param {Node} node The node
11354         */
11355         "expand" : true,
11356         /**
11357         * @event disabledchange
11358         * Fires when the disabled status of a node changes
11359         * @param {Node} node The node
11360         * @param {Boolean} disabled
11361         */
11362         "disabledchange" : true,
11363         /**
11364         * @event collapse
11365         * Fires when a node is collapsed
11366         * @param {Node} node The node
11367         */
11368         "collapse" : true,
11369         /**
11370         * @event beforeclick
11371         * Fires before click processing on a node. Return false to cancel the default action.
11372         * @param {Node} node The node
11373         * @param {Roo.EventObject} e The event object
11374         */
11375         "beforeclick":true,
11376         /**
11377         * @event checkchange
11378         * Fires when a node with a checkbox's checked property changes
11379         * @param {Node} this This node
11380         * @param {Boolean} checked
11381         */
11382         "checkchange":true,
11383         /**
11384         * @event click
11385         * Fires when a node is clicked
11386         * @param {Node} node The node
11387         * @param {Roo.EventObject} e The event object
11388         */
11389         "click":true,
11390         /**
11391         * @event dblclick
11392         * Fires when a node is double clicked
11393         * @param {Node} node The node
11394         * @param {Roo.EventObject} e The event object
11395         */
11396         "dblclick":true,
11397         /**
11398         * @event contextmenu
11399         * Fires when a node is right clicked
11400         * @param {Node} node The node
11401         * @param {Roo.EventObject} e The event object
11402         */
11403         "contextmenu":true,
11404         /**
11405         * @event beforechildrenrendered
11406         * Fires right before the child nodes for a node are rendered
11407         * @param {Node} node The node
11408         */
11409         "beforechildrenrendered":true,
11410         /**
11411         * @event startdrag
11412         * Fires when a node starts being dragged
11413         * @param {Roo.tree.TreePanel} this
11414         * @param {Roo.tree.TreeNode} node
11415         * @param {event} e The raw browser event
11416         */ 
11417        "startdrag" : true,
11418        /**
11419         * @event enddrag
11420         * Fires when a drag operation is complete
11421         * @param {Roo.tree.TreePanel} this
11422         * @param {Roo.tree.TreeNode} node
11423         * @param {event} e The raw browser event
11424         */
11425        "enddrag" : true,
11426        /**
11427         * @event dragdrop
11428         * Fires when a dragged node is dropped on a valid DD target
11429         * @param {Roo.tree.TreePanel} this
11430         * @param {Roo.tree.TreeNode} node
11431         * @param {DD} dd The dd it was dropped on
11432         * @param {event} e The raw browser event
11433         */
11434        "dragdrop" : true,
11435        /**
11436         * @event beforenodedrop
11437         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11438         * passed to handlers has the following properties:<br />
11439         * <ul style="padding:5px;padding-left:16px;">
11440         * <li>tree - The TreePanel</li>
11441         * <li>target - The node being targeted for the drop</li>
11442         * <li>data - The drag data from the drag source</li>
11443         * <li>point - The point of the drop - append, above or below</li>
11444         * <li>source - The drag source</li>
11445         * <li>rawEvent - Raw mouse event</li>
11446         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11447         * to be inserted by setting them on this object.</li>
11448         * <li>cancel - Set this to true to cancel the drop.</li>
11449         * </ul>
11450         * @param {Object} dropEvent
11451         */
11452        "beforenodedrop" : true,
11453        /**
11454         * @event nodedrop
11455         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11456         * passed to handlers has the following properties:<br />
11457         * <ul style="padding:5px;padding-left:16px;">
11458         * <li>tree - The TreePanel</li>
11459         * <li>target - The node being targeted for the drop</li>
11460         * <li>data - The drag data from the drag source</li>
11461         * <li>point - The point of the drop - append, above or below</li>
11462         * <li>source - The drag source</li>
11463         * <li>rawEvent - Raw mouse event</li>
11464         * <li>dropNode - Dropped node(s).</li>
11465         * </ul>
11466         * @param {Object} dropEvent
11467         */
11468        "nodedrop" : true,
11469         /**
11470         * @event nodedragover
11471         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11472         * passed to handlers has the following properties:<br />
11473         * <ul style="padding:5px;padding-left:16px;">
11474         * <li>tree - The TreePanel</li>
11475         * <li>target - The node being targeted for the drop</li>
11476         * <li>data - The drag data from the drag source</li>
11477         * <li>point - The point of the drop - append, above or below</li>
11478         * <li>source - The drag source</li>
11479         * <li>rawEvent - Raw mouse event</li>
11480         * <li>dropNode - Drop node(s) provided by the source.</li>
11481         * <li>cancel - Set this to true to signal drop not allowed.</li>
11482         * </ul>
11483         * @param {Object} dragOverEvent
11484         */
11485        "nodedragover" : true,
11486        /**
11487         * @event appendnode
11488         * Fires when append node to the tree
11489         * @param {Roo.tree.TreePanel} this
11490         * @param {Roo.tree.TreeNode} node
11491         * @param {Number} index The index of the newly appended node
11492         */
11493        "appendnode" : true
11494         
11495     });
11496     if(this.singleExpand){
11497        this.on("beforeexpand", this.restrictExpand, this);
11498     }
11499     if (this.editor) {
11500         this.editor.tree = this;
11501         this.editor = Roo.factory(this.editor, Roo.tree);
11502     }
11503     
11504     if (this.selModel) {
11505         this.selModel = Roo.factory(this.selModel, Roo.tree);
11506     }
11507    
11508 };
11509 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11510     rootVisible : true,
11511     animate: Roo.enableFx,
11512     lines : true,
11513     enableDD : false,
11514     hlDrop : Roo.enableFx,
11515   
11516     renderer: false,
11517     
11518     rendererTip: false,
11519     // private
11520     restrictExpand : function(node){
11521         var p = node.parentNode;
11522         if(p){
11523             if(p.expandedChild && p.expandedChild.parentNode == p){
11524                 p.expandedChild.collapse();
11525             }
11526             p.expandedChild = node;
11527         }
11528     },
11529
11530     // private override
11531     setRootNode : function(node){
11532         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11533         if(!this.rootVisible){
11534             node.ui = new Roo.tree.RootTreeNodeUI(node);
11535         }
11536         return node;
11537     },
11538
11539     /**
11540      * Returns the container element for this TreePanel
11541      */
11542     getEl : function(){
11543         return this.el;
11544     },
11545
11546     /**
11547      * Returns the default TreeLoader for this TreePanel
11548      */
11549     getLoader : function(){
11550         return this.loader;
11551     },
11552
11553     /**
11554      * Expand all nodes
11555      */
11556     expandAll : function(){
11557         this.root.expand(true);
11558     },
11559
11560     /**
11561      * Collapse all nodes
11562      */
11563     collapseAll : function(){
11564         this.root.collapse(true);
11565     },
11566
11567     /**
11568      * Returns the selection model used by this TreePanel
11569      */
11570     getSelectionModel : function(){
11571         if(!this.selModel){
11572             this.selModel = new Roo.tree.DefaultSelectionModel();
11573         }
11574         return this.selModel;
11575     },
11576
11577     /**
11578      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11579      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11580      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11581      * @return {Array}
11582      */
11583     getChecked : function(a, startNode){
11584         startNode = startNode || this.root;
11585         var r = [];
11586         var f = function(){
11587             if(this.attributes.checked){
11588                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11589             }
11590         }
11591         startNode.cascade(f);
11592         return r;
11593     },
11594
11595     /**
11596      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11597      * @param {String} path
11598      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11599      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11600      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11601      */
11602     expandPath : function(path, attr, callback){
11603         attr = attr || "id";
11604         var keys = path.split(this.pathSeparator);
11605         var curNode = this.root;
11606         if(curNode.attributes[attr] != keys[1]){ // invalid root
11607             if(callback){
11608                 callback(false, null);
11609             }
11610             return;
11611         }
11612         var index = 1;
11613         var f = function(){
11614             if(++index == keys.length){
11615                 if(callback){
11616                     callback(true, curNode);
11617                 }
11618                 return;
11619             }
11620             var c = curNode.findChild(attr, keys[index]);
11621             if(!c){
11622                 if(callback){
11623                     callback(false, curNode);
11624                 }
11625                 return;
11626             }
11627             curNode = c;
11628             c.expand(false, false, f);
11629         };
11630         curNode.expand(false, false, f);
11631     },
11632
11633     /**
11634      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11635      * @param {String} path
11636      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11637      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11638      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11639      */
11640     selectPath : function(path, attr, callback){
11641         attr = attr || "id";
11642         var keys = path.split(this.pathSeparator);
11643         var v = keys.pop();
11644         if(keys.length > 0){
11645             var f = function(success, node){
11646                 if(success && node){
11647                     var n = node.findChild(attr, v);
11648                     if(n){
11649                         n.select();
11650                         if(callback){
11651                             callback(true, n);
11652                         }
11653                     }else if(callback){
11654                         callback(false, n);
11655                     }
11656                 }else{
11657                     if(callback){
11658                         callback(false, n);
11659                     }
11660                 }
11661             };
11662             this.expandPath(keys.join(this.pathSeparator), attr, f);
11663         }else{
11664             this.root.select();
11665             if(callback){
11666                 callback(true, this.root);
11667             }
11668         }
11669     },
11670
11671     getTreeEl : function(){
11672         return this.el;
11673     },
11674
11675     /**
11676      * Trigger rendering of this TreePanel
11677      */
11678     render : function(){
11679         if (this.innerCt) {
11680             return this; // stop it rendering more than once!!
11681         }
11682         
11683         this.innerCt = this.el.createChild({tag:"ul",
11684                cls:"x-tree-root-ct " +
11685                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11686
11687         if(this.containerScroll){
11688             Roo.dd.ScrollManager.register(this.el);
11689         }
11690         if((this.enableDD || this.enableDrop) && !this.dropZone){
11691            /**
11692             * The dropZone used by this tree if drop is enabled
11693             * @type Roo.tree.TreeDropZone
11694             */
11695              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11696                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11697            });
11698         }
11699         if((this.enableDD || this.enableDrag) && !this.dragZone){
11700            /**
11701             * The dragZone used by this tree if drag is enabled
11702             * @type Roo.tree.TreeDragZone
11703             */
11704             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11705                ddGroup: this.ddGroup || "TreeDD",
11706                scroll: this.ddScroll
11707            });
11708         }
11709         this.getSelectionModel().init(this);
11710         if (!this.root) {
11711             Roo.log("ROOT not set in tree");
11712             return this;
11713         }
11714         this.root.render();
11715         if(!this.rootVisible){
11716             this.root.renderChildren();
11717         }
11718         return this;
11719     }
11720 });/*
11721  * Based on:
11722  * Ext JS Library 1.1.1
11723  * Copyright(c) 2006-2007, Ext JS, LLC.
11724  *
11725  * Originally Released Under LGPL - original licence link has changed is not relivant.
11726  *
11727  * Fork - LGPL
11728  * <script type="text/javascript">
11729  */
11730  
11731
11732 /**
11733  * @class Roo.tree.DefaultSelectionModel
11734  * @extends Roo.util.Observable
11735  * The default single selection for a TreePanel.
11736  * @param {Object} cfg Configuration
11737  */
11738 Roo.tree.DefaultSelectionModel = function(cfg){
11739    this.selNode = null;
11740    
11741    
11742    
11743    this.addEvents({
11744        /**
11745         * @event selectionchange
11746         * Fires when the selected node changes
11747         * @param {DefaultSelectionModel} this
11748         * @param {TreeNode} node the new selection
11749         */
11750        "selectionchange" : true,
11751
11752        /**
11753         * @event beforeselect
11754         * Fires before the selected node changes, return false to cancel the change
11755         * @param {DefaultSelectionModel} this
11756         * @param {TreeNode} node the new selection
11757         * @param {TreeNode} node the old selection
11758         */
11759        "beforeselect" : true
11760    });
11761    
11762     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11763 };
11764
11765 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11766     init : function(tree){
11767         this.tree = tree;
11768         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11769         tree.on("click", this.onNodeClick, this);
11770     },
11771     
11772     onNodeClick : function(node, e){
11773         if (e.ctrlKey && this.selNode == node)  {
11774             this.unselect(node);
11775             return;
11776         }
11777         this.select(node);
11778     },
11779     
11780     /**
11781      * Select a node.
11782      * @param {TreeNode} node The node to select
11783      * @return {TreeNode} The selected node
11784      */
11785     select : function(node){
11786         var last = this.selNode;
11787         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11788             if(last){
11789                 last.ui.onSelectedChange(false);
11790             }
11791             this.selNode = node;
11792             node.ui.onSelectedChange(true);
11793             this.fireEvent("selectionchange", this, node, last);
11794         }
11795         return node;
11796     },
11797     
11798     /**
11799      * Deselect a node.
11800      * @param {TreeNode} node The node to unselect
11801      */
11802     unselect : function(node){
11803         if(this.selNode == node){
11804             this.clearSelections();
11805         }    
11806     },
11807     
11808     /**
11809      * Clear all selections
11810      */
11811     clearSelections : function(){
11812         var n = this.selNode;
11813         if(n){
11814             n.ui.onSelectedChange(false);
11815             this.selNode = null;
11816             this.fireEvent("selectionchange", this, null);
11817         }
11818         return n;
11819     },
11820     
11821     /**
11822      * Get the selected node
11823      * @return {TreeNode} The selected node
11824      */
11825     getSelectedNode : function(){
11826         return this.selNode;    
11827     },
11828     
11829     /**
11830      * Returns true if the node is selected
11831      * @param {TreeNode} node The node to check
11832      * @return {Boolean}
11833      */
11834     isSelected : function(node){
11835         return this.selNode == node;  
11836     },
11837
11838     /**
11839      * Selects the node above the selected node in the tree, intelligently walking the nodes
11840      * @return TreeNode The new selection
11841      */
11842     selectPrevious : function(){
11843         var s = this.selNode || this.lastSelNode;
11844         if(!s){
11845             return null;
11846         }
11847         var ps = s.previousSibling;
11848         if(ps){
11849             if(!ps.isExpanded() || ps.childNodes.length < 1){
11850                 return this.select(ps);
11851             } else{
11852                 var lc = ps.lastChild;
11853                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11854                     lc = lc.lastChild;
11855                 }
11856                 return this.select(lc);
11857             }
11858         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11859             return this.select(s.parentNode);
11860         }
11861         return null;
11862     },
11863
11864     /**
11865      * Selects the node above the selected node in the tree, intelligently walking the nodes
11866      * @return TreeNode The new selection
11867      */
11868     selectNext : function(){
11869         var s = this.selNode || this.lastSelNode;
11870         if(!s){
11871             return null;
11872         }
11873         if(s.firstChild && s.isExpanded()){
11874              return this.select(s.firstChild);
11875          }else if(s.nextSibling){
11876              return this.select(s.nextSibling);
11877          }else if(s.parentNode){
11878             var newS = null;
11879             s.parentNode.bubble(function(){
11880                 if(this.nextSibling){
11881                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11882                     return false;
11883                 }
11884             });
11885             return newS;
11886          }
11887         return null;
11888     },
11889
11890     onKeyDown : function(e){
11891         var s = this.selNode || this.lastSelNode;
11892         // undesirable, but required
11893         var sm = this;
11894         if(!s){
11895             return;
11896         }
11897         var k = e.getKey();
11898         switch(k){
11899              case e.DOWN:
11900                  e.stopEvent();
11901                  this.selectNext();
11902              break;
11903              case e.UP:
11904                  e.stopEvent();
11905                  this.selectPrevious();
11906              break;
11907              case e.RIGHT:
11908                  e.preventDefault();
11909                  if(s.hasChildNodes()){
11910                      if(!s.isExpanded()){
11911                          s.expand();
11912                      }else if(s.firstChild){
11913                          this.select(s.firstChild, e);
11914                      }
11915                  }
11916              break;
11917              case e.LEFT:
11918                  e.preventDefault();
11919                  if(s.hasChildNodes() && s.isExpanded()){
11920                      s.collapse();
11921                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11922                      this.select(s.parentNode, e);
11923                  }
11924              break;
11925         };
11926     }
11927 });
11928
11929 /**
11930  * @class Roo.tree.MultiSelectionModel
11931  * @extends Roo.util.Observable
11932  * Multi selection for a TreePanel.
11933  * @param {Object} cfg Configuration
11934  */
11935 Roo.tree.MultiSelectionModel = function(){
11936    this.selNodes = [];
11937    this.selMap = {};
11938    this.addEvents({
11939        /**
11940         * @event selectionchange
11941         * Fires when the selected nodes change
11942         * @param {MultiSelectionModel} this
11943         * @param {Array} nodes Array of the selected nodes
11944         */
11945        "selectionchange" : true
11946    });
11947    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11948    
11949 };
11950
11951 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11952     init : function(tree){
11953         this.tree = tree;
11954         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11955         tree.on("click", this.onNodeClick, this);
11956     },
11957     
11958     onNodeClick : function(node, e){
11959         this.select(node, e, e.ctrlKey);
11960     },
11961     
11962     /**
11963      * Select a node.
11964      * @param {TreeNode} node The node to select
11965      * @param {EventObject} e (optional) An event associated with the selection
11966      * @param {Boolean} keepExisting True to retain existing selections
11967      * @return {TreeNode} The selected node
11968      */
11969     select : function(node, e, keepExisting){
11970         if(keepExisting !== true){
11971             this.clearSelections(true);
11972         }
11973         if(this.isSelected(node)){
11974             this.lastSelNode = node;
11975             return node;
11976         }
11977         this.selNodes.push(node);
11978         this.selMap[node.id] = node;
11979         this.lastSelNode = node;
11980         node.ui.onSelectedChange(true);
11981         this.fireEvent("selectionchange", this, this.selNodes);
11982         return node;
11983     },
11984     
11985     /**
11986      * Deselect a node.
11987      * @param {TreeNode} node The node to unselect
11988      */
11989     unselect : function(node){
11990         if(this.selMap[node.id]){
11991             node.ui.onSelectedChange(false);
11992             var sn = this.selNodes;
11993             var index = -1;
11994             if(sn.indexOf){
11995                 index = sn.indexOf(node);
11996             }else{
11997                 for(var i = 0, len = sn.length; i < len; i++){
11998                     if(sn[i] == node){
11999                         index = i;
12000                         break;
12001                     }
12002                 }
12003             }
12004             if(index != -1){
12005                 this.selNodes.splice(index, 1);
12006             }
12007             delete this.selMap[node.id];
12008             this.fireEvent("selectionchange", this, this.selNodes);
12009         }
12010     },
12011     
12012     /**
12013      * Clear all selections
12014      */
12015     clearSelections : function(suppressEvent){
12016         var sn = this.selNodes;
12017         if(sn.length > 0){
12018             for(var i = 0, len = sn.length; i < len; i++){
12019                 sn[i].ui.onSelectedChange(false);
12020             }
12021             this.selNodes = [];
12022             this.selMap = {};
12023             if(suppressEvent !== true){
12024                 this.fireEvent("selectionchange", this, this.selNodes);
12025             }
12026         }
12027     },
12028     
12029     /**
12030      * Returns true if the node is selected
12031      * @param {TreeNode} node The node to check
12032      * @return {Boolean}
12033      */
12034     isSelected : function(node){
12035         return this.selMap[node.id] ? true : false;  
12036     },
12037     
12038     /**
12039      * Returns an array of the selected nodes
12040      * @return {Array}
12041      */
12042     getSelectedNodes : function(){
12043         return this.selNodes;    
12044     },
12045
12046     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12047
12048     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12049
12050     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12051 });/*
12052  * Based on:
12053  * Ext JS Library 1.1.1
12054  * Copyright(c) 2006-2007, Ext JS, LLC.
12055  *
12056  * Originally Released Under LGPL - original licence link has changed is not relivant.
12057  *
12058  * Fork - LGPL
12059  * <script type="text/javascript">
12060  */
12061  
12062 /**
12063  * @class Roo.tree.TreeNode
12064  * @extends Roo.data.Node
12065  * @cfg {String} text The text for this node
12066  * @cfg {Boolean} expanded true to start the node expanded
12067  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12068  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12069  * @cfg {Boolean} disabled true to start the node disabled
12070  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12071  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12072  * @cfg {String} cls A css class to be added to the node
12073  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12074  * @cfg {String} href URL of the link used for the node (defaults to #)
12075  * @cfg {String} hrefTarget target frame for the link
12076  * @cfg {String} qtip An Ext QuickTip for the node
12077  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12078  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12079  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12080  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12081  * (defaults to undefined with no checkbox rendered)
12082  * @constructor
12083  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12084  */
12085 Roo.tree.TreeNode = function(attributes){
12086     attributes = attributes || {};
12087     if(typeof attributes == "string"){
12088         attributes = {text: attributes};
12089     }
12090     this.childrenRendered = false;
12091     this.rendered = false;
12092     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12093     this.expanded = attributes.expanded === true;
12094     this.isTarget = attributes.isTarget !== false;
12095     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12096     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12097
12098     /**
12099      * Read-only. The text for this node. To change it use setText().
12100      * @type String
12101      */
12102     this.text = attributes.text;
12103     /**
12104      * True if this node is disabled.
12105      * @type Boolean
12106      */
12107     this.disabled = attributes.disabled === true;
12108
12109     this.addEvents({
12110         /**
12111         * @event textchange
12112         * Fires when the text for this node is changed
12113         * @param {Node} this This node
12114         * @param {String} text The new text
12115         * @param {String} oldText The old text
12116         */
12117         "textchange" : true,
12118         /**
12119         * @event beforeexpand
12120         * Fires before this node is expanded, return false to cancel.
12121         * @param {Node} this This node
12122         * @param {Boolean} deep
12123         * @param {Boolean} anim
12124         */
12125         "beforeexpand" : true,
12126         /**
12127         * @event beforecollapse
12128         * Fires before this node is collapsed, return false to cancel.
12129         * @param {Node} this This node
12130         * @param {Boolean} deep
12131         * @param {Boolean} anim
12132         */
12133         "beforecollapse" : true,
12134         /**
12135         * @event expand
12136         * Fires when this node is expanded
12137         * @param {Node} this This node
12138         */
12139         "expand" : true,
12140         /**
12141         * @event disabledchange
12142         * Fires when the disabled status of this node changes
12143         * @param {Node} this This node
12144         * @param {Boolean} disabled
12145         */
12146         "disabledchange" : true,
12147         /**
12148         * @event collapse
12149         * Fires when this node is collapsed
12150         * @param {Node} this This node
12151         */
12152         "collapse" : true,
12153         /**
12154         * @event beforeclick
12155         * Fires before click processing. Return false to cancel the default action.
12156         * @param {Node} this This node
12157         * @param {Roo.EventObject} e The event object
12158         */
12159         "beforeclick":true,
12160         /**
12161         * @event checkchange
12162         * Fires when a node with a checkbox's checked property changes
12163         * @param {Node} this This node
12164         * @param {Boolean} checked
12165         */
12166         "checkchange":true,
12167         /**
12168         * @event click
12169         * Fires when this node is clicked
12170         * @param {Node} this This node
12171         * @param {Roo.EventObject} e The event object
12172         */
12173         "click":true,
12174         /**
12175         * @event dblclick
12176         * Fires when this node is double clicked
12177         * @param {Node} this This node
12178         * @param {Roo.EventObject} e The event object
12179         */
12180         "dblclick":true,
12181         /**
12182         * @event contextmenu
12183         * Fires when this node is right clicked
12184         * @param {Node} this This node
12185         * @param {Roo.EventObject} e The event object
12186         */
12187         "contextmenu":true,
12188         /**
12189         * @event beforechildrenrendered
12190         * Fires right before the child nodes for this node are rendered
12191         * @param {Node} this This node
12192         */
12193         "beforechildrenrendered":true
12194     });
12195
12196     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12197
12198     /**
12199      * Read-only. The UI for this node
12200      * @type TreeNodeUI
12201      */
12202     this.ui = new uiClass(this);
12203     
12204     // finally support items[]
12205     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12206         return;
12207     }
12208     
12209     
12210     Roo.each(this.attributes.items, function(c) {
12211         this.appendChild(Roo.factory(c,Roo.Tree));
12212     }, this);
12213     delete this.attributes.items;
12214     
12215     
12216     
12217 };
12218 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12219     preventHScroll: true,
12220     /**
12221      * Returns true if this node is expanded
12222      * @return {Boolean}
12223      */
12224     isExpanded : function(){
12225         return this.expanded;
12226     },
12227
12228     /**
12229      * Returns the UI object for this node
12230      * @return {TreeNodeUI}
12231      */
12232     getUI : function(){
12233         return this.ui;
12234     },
12235
12236     // private override
12237     setFirstChild : function(node){
12238         var of = this.firstChild;
12239         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12240         if(this.childrenRendered && of && node != of){
12241             of.renderIndent(true, true);
12242         }
12243         if(this.rendered){
12244             this.renderIndent(true, true);
12245         }
12246     },
12247
12248     // private override
12249     setLastChild : function(node){
12250         var ol = this.lastChild;
12251         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12252         if(this.childrenRendered && ol && node != ol){
12253             ol.renderIndent(true, true);
12254         }
12255         if(this.rendered){
12256             this.renderIndent(true, true);
12257         }
12258     },
12259
12260     // these methods are overridden to provide lazy rendering support
12261     // private override
12262     appendChild : function()
12263     {
12264         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12265         if(node && this.childrenRendered){
12266             node.render();
12267         }
12268         this.ui.updateExpandIcon();
12269         return node;
12270     },
12271
12272     // private override
12273     removeChild : function(node){
12274         this.ownerTree.getSelectionModel().unselect(node);
12275         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12276         // if it's been rendered remove dom node
12277         if(this.childrenRendered){
12278             node.ui.remove();
12279         }
12280         if(this.childNodes.length < 1){
12281             this.collapse(false, false);
12282         }else{
12283             this.ui.updateExpandIcon();
12284         }
12285         if(!this.firstChild) {
12286             this.childrenRendered = false;
12287         }
12288         return node;
12289     },
12290
12291     // private override
12292     insertBefore : function(node, refNode){
12293         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12294         if(newNode && refNode && this.childrenRendered){
12295             node.render();
12296         }
12297         this.ui.updateExpandIcon();
12298         return newNode;
12299     },
12300
12301     /**
12302      * Sets the text for this node
12303      * @param {String} text
12304      */
12305     setText : function(text){
12306         var oldText = this.text;
12307         this.text = text;
12308         this.attributes.text = text;
12309         if(this.rendered){ // event without subscribing
12310             this.ui.onTextChange(this, text, oldText);
12311         }
12312         this.fireEvent("textchange", this, text, oldText);
12313     },
12314
12315     /**
12316      * Triggers selection of this node
12317      */
12318     select : function(){
12319         this.getOwnerTree().getSelectionModel().select(this);
12320     },
12321
12322     /**
12323      * Triggers deselection of this node
12324      */
12325     unselect : function(){
12326         this.getOwnerTree().getSelectionModel().unselect(this);
12327     },
12328
12329     /**
12330      * Returns true if this node is selected
12331      * @return {Boolean}
12332      */
12333     isSelected : function(){
12334         return this.getOwnerTree().getSelectionModel().isSelected(this);
12335     },
12336
12337     /**
12338      * Expand this node.
12339      * @param {Boolean} deep (optional) True to expand all children as well
12340      * @param {Boolean} anim (optional) false to cancel the default animation
12341      * @param {Function} callback (optional) A callback to be called when
12342      * expanding this node completes (does not wait for deep expand to complete).
12343      * Called with 1 parameter, this node.
12344      */
12345     expand : function(deep, anim, callback){
12346         if(!this.expanded){
12347             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12348                 return;
12349             }
12350             if(!this.childrenRendered){
12351                 this.renderChildren();
12352             }
12353             this.expanded = true;
12354             
12355             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12356                 this.ui.animExpand(function(){
12357                     this.fireEvent("expand", this);
12358                     if(typeof callback == "function"){
12359                         callback(this);
12360                     }
12361                     if(deep === true){
12362                         this.expandChildNodes(true);
12363                     }
12364                 }.createDelegate(this));
12365                 return;
12366             }else{
12367                 this.ui.expand();
12368                 this.fireEvent("expand", this);
12369                 if(typeof callback == "function"){
12370                     callback(this);
12371                 }
12372             }
12373         }else{
12374            if(typeof callback == "function"){
12375                callback(this);
12376            }
12377         }
12378         if(deep === true){
12379             this.expandChildNodes(true);
12380         }
12381     },
12382
12383     isHiddenRoot : function(){
12384         return this.isRoot && !this.getOwnerTree().rootVisible;
12385     },
12386
12387     /**
12388      * Collapse this node.
12389      * @param {Boolean} deep (optional) True to collapse all children as well
12390      * @param {Boolean} anim (optional) false to cancel the default animation
12391      */
12392     collapse : function(deep, anim){
12393         if(this.expanded && !this.isHiddenRoot()){
12394             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12395                 return;
12396             }
12397             this.expanded = false;
12398             if((this.getOwnerTree().animate && anim !== false) || anim){
12399                 this.ui.animCollapse(function(){
12400                     this.fireEvent("collapse", this);
12401                     if(deep === true){
12402                         this.collapseChildNodes(true);
12403                     }
12404                 }.createDelegate(this));
12405                 return;
12406             }else{
12407                 this.ui.collapse();
12408                 this.fireEvent("collapse", this);
12409             }
12410         }
12411         if(deep === true){
12412             var cs = this.childNodes;
12413             for(var i = 0, len = cs.length; i < len; i++) {
12414                 cs[i].collapse(true, false);
12415             }
12416         }
12417     },
12418
12419     // private
12420     delayedExpand : function(delay){
12421         if(!this.expandProcId){
12422             this.expandProcId = this.expand.defer(delay, this);
12423         }
12424     },
12425
12426     // private
12427     cancelExpand : function(){
12428         if(this.expandProcId){
12429             clearTimeout(this.expandProcId);
12430         }
12431         this.expandProcId = false;
12432     },
12433
12434     /**
12435      * Toggles expanded/collapsed state of the node
12436      */
12437     toggle : function(){
12438         if(this.expanded){
12439             this.collapse();
12440         }else{
12441             this.expand();
12442         }
12443     },
12444
12445     /**
12446      * Ensures all parent nodes are expanded
12447      */
12448     ensureVisible : function(callback){
12449         var tree = this.getOwnerTree();
12450         tree.expandPath(this.parentNode.getPath(), false, function(){
12451             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12452             Roo.callback(callback);
12453         }.createDelegate(this));
12454     },
12455
12456     /**
12457      * Expand all child nodes
12458      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12459      */
12460     expandChildNodes : function(deep){
12461         var cs = this.childNodes;
12462         for(var i = 0, len = cs.length; i < len; i++) {
12463                 cs[i].expand(deep);
12464         }
12465     },
12466
12467     /**
12468      * Collapse all child nodes
12469      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12470      */
12471     collapseChildNodes : function(deep){
12472         var cs = this.childNodes;
12473         for(var i = 0, len = cs.length; i < len; i++) {
12474                 cs[i].collapse(deep);
12475         }
12476     },
12477
12478     /**
12479      * Disables this node
12480      */
12481     disable : function(){
12482         this.disabled = true;
12483         this.unselect();
12484         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12485             this.ui.onDisableChange(this, true);
12486         }
12487         this.fireEvent("disabledchange", this, true);
12488     },
12489
12490     /**
12491      * Enables this node
12492      */
12493     enable : function(){
12494         this.disabled = false;
12495         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12496             this.ui.onDisableChange(this, false);
12497         }
12498         this.fireEvent("disabledchange", this, false);
12499     },
12500
12501     // private
12502     renderChildren : function(suppressEvent){
12503         if(suppressEvent !== false){
12504             this.fireEvent("beforechildrenrendered", this);
12505         }
12506         var cs = this.childNodes;
12507         for(var i = 0, len = cs.length; i < len; i++){
12508             cs[i].render(true);
12509         }
12510         this.childrenRendered = true;
12511     },
12512
12513     // private
12514     sort : function(fn, scope){
12515         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12516         if(this.childrenRendered){
12517             var cs = this.childNodes;
12518             for(var i = 0, len = cs.length; i < len; i++){
12519                 cs[i].render(true);
12520             }
12521         }
12522     },
12523
12524     // private
12525     render : function(bulkRender){
12526         this.ui.render(bulkRender);
12527         if(!this.rendered){
12528             this.rendered = true;
12529             if(this.expanded){
12530                 this.expanded = false;
12531                 this.expand(false, false);
12532             }
12533         }
12534     },
12535
12536     // private
12537     renderIndent : function(deep, refresh){
12538         if(refresh){
12539             this.ui.childIndent = null;
12540         }
12541         this.ui.renderIndent();
12542         if(deep === true && this.childrenRendered){
12543             var cs = this.childNodes;
12544             for(var i = 0, len = cs.length; i < len; i++){
12545                 cs[i].renderIndent(true, refresh);
12546             }
12547         }
12548     }
12549 });/*
12550  * Based on:
12551  * Ext JS Library 1.1.1
12552  * Copyright(c) 2006-2007, Ext JS, LLC.
12553  *
12554  * Originally Released Under LGPL - original licence link has changed is not relivant.
12555  *
12556  * Fork - LGPL
12557  * <script type="text/javascript">
12558  */
12559  
12560 /**
12561  * @class Roo.tree.AsyncTreeNode
12562  * @extends Roo.tree.TreeNode
12563  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12564  * @constructor
12565  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12566  */
12567  Roo.tree.AsyncTreeNode = function(config){
12568     this.loaded = false;
12569     this.loading = false;
12570     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12571     /**
12572     * @event beforeload
12573     * Fires before this node is loaded, return false to cancel
12574     * @param {Node} this This node
12575     */
12576     this.addEvents({'beforeload':true, 'load': true});
12577     /**
12578     * @event load
12579     * Fires when this node is loaded
12580     * @param {Node} this This node
12581     */
12582     /**
12583      * The loader used by this node (defaults to using the tree's defined loader)
12584      * @type TreeLoader
12585      * @property loader
12586      */
12587 };
12588 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12589     expand : function(deep, anim, callback){
12590         if(this.loading){ // if an async load is already running, waiting til it's done
12591             var timer;
12592             var f = function(){
12593                 if(!this.loading){ // done loading
12594                     clearInterval(timer);
12595                     this.expand(deep, anim, callback);
12596                 }
12597             }.createDelegate(this);
12598             timer = setInterval(f, 200);
12599             return;
12600         }
12601         if(!this.loaded){
12602             if(this.fireEvent("beforeload", this) === false){
12603                 return;
12604             }
12605             this.loading = true;
12606             this.ui.beforeLoad(this);
12607             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12608             if(loader){
12609                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12610                 return;
12611             }
12612         }
12613         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12614     },
12615     
12616     /**
12617      * Returns true if this node is currently loading
12618      * @return {Boolean}
12619      */
12620     isLoading : function(){
12621         return this.loading;  
12622     },
12623     
12624     loadComplete : function(deep, anim, callback){
12625         this.loading = false;
12626         this.loaded = true;
12627         this.ui.afterLoad(this);
12628         this.fireEvent("load", this);
12629         this.expand(deep, anim, callback);
12630     },
12631     
12632     /**
12633      * Returns true if this node has been loaded
12634      * @return {Boolean}
12635      */
12636     isLoaded : function(){
12637         return this.loaded;
12638     },
12639     
12640     hasChildNodes : function(){
12641         if(!this.isLeaf() && !this.loaded){
12642             return true;
12643         }else{
12644             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12645         }
12646     },
12647
12648     /**
12649      * Trigger a reload for this node
12650      * @param {Function} callback
12651      */
12652     reload : function(callback){
12653         this.collapse(false, false);
12654         while(this.firstChild){
12655             this.removeChild(this.firstChild);
12656         }
12657         this.childrenRendered = false;
12658         this.loaded = false;
12659         if(this.isHiddenRoot()){
12660             this.expanded = false;
12661         }
12662         this.expand(false, false, callback);
12663     }
12664 });/*
12665  * Based on:
12666  * Ext JS Library 1.1.1
12667  * Copyright(c) 2006-2007, Ext JS, LLC.
12668  *
12669  * Originally Released Under LGPL - original licence link has changed is not relivant.
12670  *
12671  * Fork - LGPL
12672  * <script type="text/javascript">
12673  */
12674  
12675 /**
12676  * @class Roo.tree.TreeNodeUI
12677  * @constructor
12678  * @param {Object} node The node to render
12679  * The TreeNode UI implementation is separate from the
12680  * tree implementation. Unless you are customizing the tree UI,
12681  * you should never have to use this directly.
12682  */
12683 Roo.tree.TreeNodeUI = function(node){
12684     this.node = node;
12685     this.rendered = false;
12686     this.animating = false;
12687     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12688 };
12689
12690 Roo.tree.TreeNodeUI.prototype = {
12691     removeChild : function(node){
12692         if(this.rendered){
12693             this.ctNode.removeChild(node.ui.getEl());
12694         }
12695     },
12696
12697     beforeLoad : function(){
12698          this.addClass("x-tree-node-loading");
12699     },
12700
12701     afterLoad : function(){
12702          this.removeClass("x-tree-node-loading");
12703     },
12704
12705     onTextChange : function(node, text, oldText){
12706         if(this.rendered){
12707             this.textNode.innerHTML = text;
12708         }
12709     },
12710
12711     onDisableChange : function(node, state){
12712         this.disabled = state;
12713         if(state){
12714             this.addClass("x-tree-node-disabled");
12715         }else{
12716             this.removeClass("x-tree-node-disabled");
12717         }
12718     },
12719
12720     onSelectedChange : function(state){
12721         if(state){
12722             this.focus();
12723             this.addClass("x-tree-selected");
12724         }else{
12725             //this.blur();
12726             this.removeClass("x-tree-selected");
12727         }
12728     },
12729
12730     onMove : function(tree, node, oldParent, newParent, index, refNode){
12731         this.childIndent = null;
12732         if(this.rendered){
12733             var targetNode = newParent.ui.getContainer();
12734             if(!targetNode){//target not rendered
12735                 this.holder = document.createElement("div");
12736                 this.holder.appendChild(this.wrap);
12737                 return;
12738             }
12739             var insertBefore = refNode ? refNode.ui.getEl() : null;
12740             if(insertBefore){
12741                 targetNode.insertBefore(this.wrap, insertBefore);
12742             }else{
12743                 targetNode.appendChild(this.wrap);
12744             }
12745             this.node.renderIndent(true);
12746         }
12747     },
12748
12749     addClass : function(cls){
12750         if(this.elNode){
12751             Roo.fly(this.elNode).addClass(cls);
12752         }
12753     },
12754
12755     removeClass : function(cls){
12756         if(this.elNode){
12757             Roo.fly(this.elNode).removeClass(cls);
12758         }
12759     },
12760
12761     remove : function(){
12762         if(this.rendered){
12763             this.holder = document.createElement("div");
12764             this.holder.appendChild(this.wrap);
12765         }
12766     },
12767
12768     fireEvent : function(){
12769         return this.node.fireEvent.apply(this.node, arguments);
12770     },
12771
12772     initEvents : function(){
12773         this.node.on("move", this.onMove, this);
12774         var E = Roo.EventManager;
12775         var a = this.anchor;
12776
12777         var el = Roo.fly(a, '_treeui');
12778
12779         if(Roo.isOpera){ // opera render bug ignores the CSS
12780             el.setStyle("text-decoration", "none");
12781         }
12782
12783         el.on("click", this.onClick, this);
12784         el.on("dblclick", this.onDblClick, this);
12785
12786         if(this.checkbox){
12787             Roo.EventManager.on(this.checkbox,
12788                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12789         }
12790
12791         el.on("contextmenu", this.onContextMenu, this);
12792
12793         var icon = Roo.fly(this.iconNode);
12794         icon.on("click", this.onClick, this);
12795         icon.on("dblclick", this.onDblClick, this);
12796         icon.on("contextmenu", this.onContextMenu, this);
12797         E.on(this.ecNode, "click", this.ecClick, this, true);
12798
12799         if(this.node.disabled){
12800             this.addClass("x-tree-node-disabled");
12801         }
12802         if(this.node.hidden){
12803             this.addClass("x-tree-node-disabled");
12804         }
12805         var ot = this.node.getOwnerTree();
12806         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12807         if(dd && (!this.node.isRoot || ot.rootVisible)){
12808             Roo.dd.Registry.register(this.elNode, {
12809                 node: this.node,
12810                 handles: this.getDDHandles(),
12811                 isHandle: false
12812             });
12813         }
12814     },
12815
12816     getDDHandles : function(){
12817         return [this.iconNode, this.textNode];
12818     },
12819
12820     hide : function(){
12821         if(this.rendered){
12822             this.wrap.style.display = "none";
12823         }
12824     },
12825
12826     show : function(){
12827         if(this.rendered){
12828             this.wrap.style.display = "";
12829         }
12830     },
12831
12832     onContextMenu : function(e){
12833         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12834             e.preventDefault();
12835             this.focus();
12836             this.fireEvent("contextmenu", this.node, e);
12837         }
12838     },
12839
12840     onClick : function(e){
12841         if(this.dropping){
12842             e.stopEvent();
12843             return;
12844         }
12845         if(this.fireEvent("beforeclick", this.node, e) !== false){
12846             if(!this.disabled && this.node.attributes.href){
12847                 this.fireEvent("click", this.node, e);
12848                 return;
12849             }
12850             e.preventDefault();
12851             if(this.disabled){
12852                 return;
12853             }
12854
12855             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12856                 this.node.toggle();
12857             }
12858
12859             this.fireEvent("click", this.node, e);
12860         }else{
12861             e.stopEvent();
12862         }
12863     },
12864
12865     onDblClick : function(e){
12866         e.preventDefault();
12867         if(this.disabled){
12868             return;
12869         }
12870         if(this.checkbox){
12871             this.toggleCheck();
12872         }
12873         if(!this.animating && this.node.hasChildNodes()){
12874             this.node.toggle();
12875         }
12876         this.fireEvent("dblclick", this.node, e);
12877     },
12878
12879     onCheckChange : function(){
12880         var checked = this.checkbox.checked;
12881         this.node.attributes.checked = checked;
12882         this.fireEvent('checkchange', this.node, checked);
12883     },
12884
12885     ecClick : function(e){
12886         if(!this.animating && this.node.hasChildNodes()){
12887             this.node.toggle();
12888         }
12889     },
12890
12891     startDrop : function(){
12892         this.dropping = true;
12893     },
12894
12895     // delayed drop so the click event doesn't get fired on a drop
12896     endDrop : function(){
12897        setTimeout(function(){
12898            this.dropping = false;
12899        }.createDelegate(this), 50);
12900     },
12901
12902     expand : function(){
12903         this.updateExpandIcon();
12904         this.ctNode.style.display = "";
12905     },
12906
12907     focus : function(){
12908         if(!this.node.preventHScroll){
12909             try{this.anchor.focus();
12910             }catch(e){}
12911         }else if(!Roo.isIE){
12912             try{
12913                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12914                 var l = noscroll.scrollLeft;
12915                 this.anchor.focus();
12916                 noscroll.scrollLeft = l;
12917             }catch(e){}
12918         }
12919     },
12920
12921     toggleCheck : function(value){
12922         var cb = this.checkbox;
12923         if(cb){
12924             cb.checked = (value === undefined ? !cb.checked : value);
12925         }
12926     },
12927
12928     blur : function(){
12929         try{
12930             this.anchor.blur();
12931         }catch(e){}
12932     },
12933
12934     animExpand : function(callback){
12935         var ct = Roo.get(this.ctNode);
12936         ct.stopFx();
12937         if(!this.node.hasChildNodes()){
12938             this.updateExpandIcon();
12939             this.ctNode.style.display = "";
12940             Roo.callback(callback);
12941             return;
12942         }
12943         this.animating = true;
12944         this.updateExpandIcon();
12945
12946         ct.slideIn('t', {
12947            callback : function(){
12948                this.animating = false;
12949                Roo.callback(callback);
12950             },
12951             scope: this,
12952             duration: this.node.ownerTree.duration || .25
12953         });
12954     },
12955
12956     highlight : function(){
12957         var tree = this.node.getOwnerTree();
12958         Roo.fly(this.wrap).highlight(
12959             tree.hlColor || "C3DAF9",
12960             {endColor: tree.hlBaseColor}
12961         );
12962     },
12963
12964     collapse : function(){
12965         this.updateExpandIcon();
12966         this.ctNode.style.display = "none";
12967     },
12968
12969     animCollapse : function(callback){
12970         var ct = Roo.get(this.ctNode);
12971         ct.enableDisplayMode('block');
12972         ct.stopFx();
12973
12974         this.animating = true;
12975         this.updateExpandIcon();
12976
12977         ct.slideOut('t', {
12978             callback : function(){
12979                this.animating = false;
12980                Roo.callback(callback);
12981             },
12982             scope: this,
12983             duration: this.node.ownerTree.duration || .25
12984         });
12985     },
12986
12987     getContainer : function(){
12988         return this.ctNode;
12989     },
12990
12991     getEl : function(){
12992         return this.wrap;
12993     },
12994
12995     appendDDGhost : function(ghostNode){
12996         ghostNode.appendChild(this.elNode.cloneNode(true));
12997     },
12998
12999     getDDRepairXY : function(){
13000         return Roo.lib.Dom.getXY(this.iconNode);
13001     },
13002
13003     onRender : function(){
13004         this.render();
13005     },
13006
13007     render : function(bulkRender){
13008         var n = this.node, a = n.attributes;
13009         var targetNode = n.parentNode ?
13010               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13011
13012         if(!this.rendered){
13013             this.rendered = true;
13014
13015             this.renderElements(n, a, targetNode, bulkRender);
13016
13017             if(a.qtip){
13018                if(this.textNode.setAttributeNS){
13019                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13020                    if(a.qtipTitle){
13021                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13022                    }
13023                }else{
13024                    this.textNode.setAttribute("ext:qtip", a.qtip);
13025                    if(a.qtipTitle){
13026                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13027                    }
13028                }
13029             }else if(a.qtipCfg){
13030                 a.qtipCfg.target = Roo.id(this.textNode);
13031                 Roo.QuickTips.register(a.qtipCfg);
13032             }
13033             this.initEvents();
13034             if(!this.node.expanded){
13035                 this.updateExpandIcon();
13036             }
13037         }else{
13038             if(bulkRender === true) {
13039                 targetNode.appendChild(this.wrap);
13040             }
13041         }
13042     },
13043
13044     renderElements : function(n, a, targetNode, bulkRender)
13045     {
13046         // add some indent caching, this helps performance when rendering a large tree
13047         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13048         var t = n.getOwnerTree();
13049         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13050         if (typeof(n.attributes.html) != 'undefined') {
13051             txt = n.attributes.html;
13052         }
13053         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13054         var cb = typeof a.checked == 'boolean';
13055         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13056         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13057             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13058             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13059             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13060             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13061             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13062              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13063                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13064             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13065             "</li>"];
13066
13067         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13068             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13069                                 n.nextSibling.ui.getEl(), buf.join(""));
13070         }else{
13071             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13072         }
13073
13074         this.elNode = this.wrap.childNodes[0];
13075         this.ctNode = this.wrap.childNodes[1];
13076         var cs = this.elNode.childNodes;
13077         this.indentNode = cs[0];
13078         this.ecNode = cs[1];
13079         this.iconNode = cs[2];
13080         var index = 3;
13081         if(cb){
13082             this.checkbox = cs[3];
13083             index++;
13084         }
13085         this.anchor = cs[index];
13086         this.textNode = cs[index].firstChild;
13087     },
13088
13089     getAnchor : function(){
13090         return this.anchor;
13091     },
13092
13093     getTextEl : function(){
13094         return this.textNode;
13095     },
13096
13097     getIconEl : function(){
13098         return this.iconNode;
13099     },
13100
13101     isChecked : function(){
13102         return this.checkbox ? this.checkbox.checked : false;
13103     },
13104
13105     updateExpandIcon : function(){
13106         if(this.rendered){
13107             var n = this.node, c1, c2;
13108             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13109             var hasChild = n.hasChildNodes();
13110             if(hasChild){
13111                 if(n.expanded){
13112                     cls += "-minus";
13113                     c1 = "x-tree-node-collapsed";
13114                     c2 = "x-tree-node-expanded";
13115                 }else{
13116                     cls += "-plus";
13117                     c1 = "x-tree-node-expanded";
13118                     c2 = "x-tree-node-collapsed";
13119                 }
13120                 if(this.wasLeaf){
13121                     this.removeClass("x-tree-node-leaf");
13122                     this.wasLeaf = false;
13123                 }
13124                 if(this.c1 != c1 || this.c2 != c2){
13125                     Roo.fly(this.elNode).replaceClass(c1, c2);
13126                     this.c1 = c1; this.c2 = c2;
13127                 }
13128             }else{
13129                 // this changes non-leafs into leafs if they have no children.
13130                 // it's not very rational behaviour..
13131                 
13132                 if(!this.wasLeaf && this.node.leaf){
13133                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13134                     delete this.c1;
13135                     delete this.c2;
13136                     this.wasLeaf = true;
13137                 }
13138             }
13139             var ecc = "x-tree-ec-icon "+cls;
13140             if(this.ecc != ecc){
13141                 this.ecNode.className = ecc;
13142                 this.ecc = ecc;
13143             }
13144         }
13145     },
13146
13147     getChildIndent : function(){
13148         if(!this.childIndent){
13149             var buf = [];
13150             var p = this.node;
13151             while(p){
13152                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13153                     if(!p.isLast()) {
13154                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13155                     } else {
13156                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13157                     }
13158                 }
13159                 p = p.parentNode;
13160             }
13161             this.childIndent = buf.join("");
13162         }
13163         return this.childIndent;
13164     },
13165
13166     renderIndent : function(){
13167         if(this.rendered){
13168             var indent = "";
13169             var p = this.node.parentNode;
13170             if(p){
13171                 indent = p.ui.getChildIndent();
13172             }
13173             if(this.indentMarkup != indent){ // don't rerender if not required
13174                 this.indentNode.innerHTML = indent;
13175                 this.indentMarkup = indent;
13176             }
13177             this.updateExpandIcon();
13178         }
13179     }
13180 };
13181
13182 Roo.tree.RootTreeNodeUI = function(){
13183     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13184 };
13185 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13186     render : function(){
13187         if(!this.rendered){
13188             var targetNode = this.node.ownerTree.innerCt.dom;
13189             this.node.expanded = true;
13190             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13191             this.wrap = this.ctNode = targetNode.firstChild;
13192         }
13193     },
13194     collapse : function(){
13195     },
13196     expand : function(){
13197     }
13198 });/*
13199  * Based on:
13200  * Ext JS Library 1.1.1
13201  * Copyright(c) 2006-2007, Ext JS, LLC.
13202  *
13203  * Originally Released Under LGPL - original licence link has changed is not relivant.
13204  *
13205  * Fork - LGPL
13206  * <script type="text/javascript">
13207  */
13208 /**
13209  * @class Roo.tree.TreeLoader
13210  * @extends Roo.util.Observable
13211  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13212  * nodes from a specified URL. The response must be a javascript Array definition
13213  * who's elements are node definition objects. eg:
13214  * <pre><code>
13215 {  success : true,
13216    data :      [
13217    
13218     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13219     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13220     ]
13221 }
13222
13223
13224 </code></pre>
13225  * <br><br>
13226  * The old style respose with just an array is still supported, but not recommended.
13227  * <br><br>
13228  *
13229  * A server request is sent, and child nodes are loaded only when a node is expanded.
13230  * The loading node's id is passed to the server under the parameter name "node" to
13231  * enable the server to produce the correct child nodes.
13232  * <br><br>
13233  * To pass extra parameters, an event handler may be attached to the "beforeload"
13234  * event, and the parameters specified in the TreeLoader's baseParams property:
13235  * <pre><code>
13236     myTreeLoader.on("beforeload", function(treeLoader, node) {
13237         this.baseParams.category = node.attributes.category;
13238     }, this);
13239     
13240 </code></pre>
13241  *
13242  * This would pass an HTTP parameter called "category" to the server containing
13243  * the value of the Node's "category" attribute.
13244  * @constructor
13245  * Creates a new Treeloader.
13246  * @param {Object} config A config object containing config properties.
13247  */
13248 Roo.tree.TreeLoader = function(config){
13249     this.baseParams = {};
13250     this.requestMethod = "POST";
13251     Roo.apply(this, config);
13252
13253     this.addEvents({
13254     
13255         /**
13256          * @event beforeload
13257          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13258          * @param {Object} This TreeLoader object.
13259          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13260          * @param {Object} callback The callback function specified in the {@link #load} call.
13261          */
13262         beforeload : true,
13263         /**
13264          * @event load
13265          * Fires when the node has been successfuly loaded.
13266          * @param {Object} This TreeLoader object.
13267          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13268          * @param {Object} response The response object containing the data from the server.
13269          */
13270         load : true,
13271         /**
13272          * @event loadexception
13273          * Fires if the network request failed.
13274          * @param {Object} This TreeLoader object.
13275          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13276          * @param {Object} response The response object containing the data from the server.
13277          */
13278         loadexception : true,
13279         /**
13280          * @event create
13281          * Fires before a node is created, enabling you to return custom Node types 
13282          * @param {Object} This TreeLoader object.
13283          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13284          */
13285         create : true
13286     });
13287
13288     Roo.tree.TreeLoader.superclass.constructor.call(this);
13289 };
13290
13291 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13292     /**
13293     * @cfg {String} dataUrl The URL from which to request a Json string which
13294     * specifies an array of node definition object representing the child nodes
13295     * to be loaded.
13296     */
13297     /**
13298     * @cfg {String} requestMethod either GET or POST
13299     * defaults to POST (due to BC)
13300     * to be loaded.
13301     */
13302     /**
13303     * @cfg {Object} baseParams (optional) An object containing properties which
13304     * specify HTTP parameters to be passed to each request for child nodes.
13305     */
13306     /**
13307     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13308     * created by this loader. If the attributes sent by the server have an attribute in this object,
13309     * they take priority.
13310     */
13311     /**
13312     * @cfg {Object} uiProviders (optional) An object containing properties which
13313     * 
13314     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13315     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13316     * <i>uiProvider</i> attribute of a returned child node is a string rather
13317     * than a reference to a TreeNodeUI implementation, this that string value
13318     * is used as a property name in the uiProviders object. You can define the provider named
13319     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13320     */
13321     uiProviders : {},
13322
13323     /**
13324     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13325     * child nodes before loading.
13326     */
13327     clearOnLoad : true,
13328
13329     /**
13330     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13331     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13332     * Grid query { data : [ .....] }
13333     */
13334     
13335     root : false,
13336      /**
13337     * @cfg {String} queryParam (optional) 
13338     * Name of the query as it will be passed on the querystring (defaults to 'node')
13339     * eg. the request will be ?node=[id]
13340     */
13341     
13342     
13343     queryParam: false,
13344     
13345     /**
13346      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13347      * This is called automatically when a node is expanded, but may be used to reload
13348      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13349      * @param {Roo.tree.TreeNode} node
13350      * @param {Function} callback
13351      */
13352     load : function(node, callback){
13353         if(this.clearOnLoad){
13354             while(node.firstChild){
13355                 node.removeChild(node.firstChild);
13356             }
13357         }
13358         if(node.attributes.children){ // preloaded json children
13359             var cs = node.attributes.children;
13360             for(var i = 0, len = cs.length; i < len; i++){
13361                 node.appendChild(this.createNode(cs[i]));
13362             }
13363             if(typeof callback == "function"){
13364                 callback();
13365             }
13366         }else if(this.dataUrl){
13367             this.requestData(node, callback);
13368         }
13369     },
13370
13371     getParams: function(node){
13372         var buf = [], bp = this.baseParams;
13373         for(var key in bp){
13374             if(typeof bp[key] != "function"){
13375                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13376             }
13377         }
13378         var n = this.queryParam === false ? 'node' : this.queryParam;
13379         buf.push(n + "=", encodeURIComponent(node.id));
13380         return buf.join("");
13381     },
13382
13383     requestData : function(node, callback){
13384         if(this.fireEvent("beforeload", this, node, callback) !== false){
13385             this.transId = Roo.Ajax.request({
13386                 method:this.requestMethod,
13387                 url: this.dataUrl||this.url,
13388                 success: this.handleResponse,
13389                 failure: this.handleFailure,
13390                 scope: this,
13391                 argument: {callback: callback, node: node},
13392                 params: this.getParams(node)
13393             });
13394         }else{
13395             // if the load is cancelled, make sure we notify
13396             // the node that we are done
13397             if(typeof callback == "function"){
13398                 callback();
13399             }
13400         }
13401     },
13402
13403     isLoading : function(){
13404         return this.transId ? true : false;
13405     },
13406
13407     abort : function(){
13408         if(this.isLoading()){
13409             Roo.Ajax.abort(this.transId);
13410         }
13411     },
13412
13413     // private
13414     createNode : function(attr)
13415     {
13416         // apply baseAttrs, nice idea Corey!
13417         if(this.baseAttrs){
13418             Roo.applyIf(attr, this.baseAttrs);
13419         }
13420         if(this.applyLoader !== false){
13421             attr.loader = this;
13422         }
13423         // uiProvider = depreciated..
13424         
13425         if(typeof(attr.uiProvider) == 'string'){
13426            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13427                 /**  eval:var:attr */ eval(attr.uiProvider);
13428         }
13429         if(typeof(this.uiProviders['default']) != 'undefined') {
13430             attr.uiProvider = this.uiProviders['default'];
13431         }
13432         
13433         this.fireEvent('create', this, attr);
13434         
13435         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13436         return(attr.leaf ?
13437                         new Roo.tree.TreeNode(attr) :
13438                         new Roo.tree.AsyncTreeNode(attr));
13439     },
13440
13441     processResponse : function(response, node, callback)
13442     {
13443         var json = response.responseText;
13444         try {
13445             
13446             var o = Roo.decode(json);
13447             
13448             if (this.root === false && typeof(o.success) != undefined) {
13449                 this.root = 'data'; // the default behaviour for list like data..
13450                 }
13451                 
13452             if (this.root !== false &&  !o.success) {
13453                 // it's a failure condition.
13454                 var a = response.argument;
13455                 this.fireEvent("loadexception", this, a.node, response);
13456                 Roo.log("Load failed - should have a handler really");
13457                 return;
13458             }
13459             
13460             
13461             
13462             if (this.root !== false) {
13463                  o = o[this.root];
13464             }
13465             
13466             for(var i = 0, len = o.length; i < len; i++){
13467                 var n = this.createNode(o[i]);
13468                 if(n){
13469                     node.appendChild(n);
13470                 }
13471             }
13472             if(typeof callback == "function"){
13473                 callback(this, node);
13474             }
13475         }catch(e){
13476             this.handleFailure(response);
13477         }
13478     },
13479
13480     handleResponse : function(response){
13481         this.transId = false;
13482         var a = response.argument;
13483         this.processResponse(response, a.node, a.callback);
13484         this.fireEvent("load", this, a.node, response);
13485     },
13486
13487     handleFailure : function(response)
13488     {
13489         // should handle failure better..
13490         this.transId = false;
13491         var a = response.argument;
13492         this.fireEvent("loadexception", this, a.node, response);
13493         if(typeof a.callback == "function"){
13494             a.callback(this, a.node);
13495         }
13496     }
13497 });/*
13498  * Based on:
13499  * Ext JS Library 1.1.1
13500  * Copyright(c) 2006-2007, Ext JS, LLC.
13501  *
13502  * Originally Released Under LGPL - original licence link has changed is not relivant.
13503  *
13504  * Fork - LGPL
13505  * <script type="text/javascript">
13506  */
13507
13508 /**
13509 * @class Roo.tree.TreeFilter
13510 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13511 * @param {TreePanel} tree
13512 * @param {Object} config (optional)
13513  */
13514 Roo.tree.TreeFilter = function(tree, config){
13515     this.tree = tree;
13516     this.filtered = {};
13517     Roo.apply(this, config);
13518 };
13519
13520 Roo.tree.TreeFilter.prototype = {
13521     clearBlank:false,
13522     reverse:false,
13523     autoClear:false,
13524     remove:false,
13525
13526      /**
13527      * Filter the data by a specific attribute.
13528      * @param {String/RegExp} value Either string that the attribute value
13529      * should start with or a RegExp to test against the attribute
13530      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13531      * @param {TreeNode} startNode (optional) The node to start the filter at.
13532      */
13533     filter : function(value, attr, startNode){
13534         attr = attr || "text";
13535         var f;
13536         if(typeof value == "string"){
13537             var vlen = value.length;
13538             // auto clear empty filter
13539             if(vlen == 0 && this.clearBlank){
13540                 this.clear();
13541                 return;
13542             }
13543             value = value.toLowerCase();
13544             f = function(n){
13545                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13546             };
13547         }else if(value.exec){ // regex?
13548             f = function(n){
13549                 return value.test(n.attributes[attr]);
13550             };
13551         }else{
13552             throw 'Illegal filter type, must be string or regex';
13553         }
13554         this.filterBy(f, null, startNode);
13555         },
13556
13557     /**
13558      * Filter by a function. The passed function will be called with each
13559      * node in the tree (or from the startNode). If the function returns true, the node is kept
13560      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13561      * @param {Function} fn The filter function
13562      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13563      */
13564     filterBy : function(fn, scope, startNode){
13565         startNode = startNode || this.tree.root;
13566         if(this.autoClear){
13567             this.clear();
13568         }
13569         var af = this.filtered, rv = this.reverse;
13570         var f = function(n){
13571             if(n == startNode){
13572                 return true;
13573             }
13574             if(af[n.id]){
13575                 return false;
13576             }
13577             var m = fn.call(scope || n, n);
13578             if(!m || rv){
13579                 af[n.id] = n;
13580                 n.ui.hide();
13581                 return false;
13582             }
13583             return true;
13584         };
13585         startNode.cascade(f);
13586         if(this.remove){
13587            for(var id in af){
13588                if(typeof id != "function"){
13589                    var n = af[id];
13590                    if(n && n.parentNode){
13591                        n.parentNode.removeChild(n);
13592                    }
13593                }
13594            }
13595         }
13596     },
13597
13598     /**
13599      * Clears the current filter. Note: with the "remove" option
13600      * set a filter cannot be cleared.
13601      */
13602     clear : function(){
13603         var t = this.tree;
13604         var af = this.filtered;
13605         for(var id in af){
13606             if(typeof id != "function"){
13607                 var n = af[id];
13608                 if(n){
13609                     n.ui.show();
13610                 }
13611             }
13612         }
13613         this.filtered = {};
13614     }
13615 };
13616 /*
13617  * Based on:
13618  * Ext JS Library 1.1.1
13619  * Copyright(c) 2006-2007, Ext JS, LLC.
13620  *
13621  * Originally Released Under LGPL - original licence link has changed is not relivant.
13622  *
13623  * Fork - LGPL
13624  * <script type="text/javascript">
13625  */
13626  
13627
13628 /**
13629  * @class Roo.tree.TreeSorter
13630  * Provides sorting of nodes in a TreePanel
13631  * 
13632  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13633  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13634  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13635  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13636  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13637  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13638  * @constructor
13639  * @param {TreePanel} tree
13640  * @param {Object} config
13641  */
13642 Roo.tree.TreeSorter = function(tree, config){
13643     Roo.apply(this, config);
13644     tree.on("beforechildrenrendered", this.doSort, this);
13645     tree.on("append", this.updateSort, this);
13646     tree.on("insert", this.updateSort, this);
13647     
13648     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13649     var p = this.property || "text";
13650     var sortType = this.sortType;
13651     var fs = this.folderSort;
13652     var cs = this.caseSensitive === true;
13653     var leafAttr = this.leafAttr || 'leaf';
13654
13655     this.sortFn = function(n1, n2){
13656         if(fs){
13657             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13658                 return 1;
13659             }
13660             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13661                 return -1;
13662             }
13663         }
13664         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13665         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13666         if(v1 < v2){
13667                         return dsc ? +1 : -1;
13668                 }else if(v1 > v2){
13669                         return dsc ? -1 : +1;
13670         }else{
13671                 return 0;
13672         }
13673     };
13674 };
13675
13676 Roo.tree.TreeSorter.prototype = {
13677     doSort : function(node){
13678         node.sort(this.sortFn);
13679     },
13680     
13681     compareNodes : function(n1, n2){
13682         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13683     },
13684     
13685     updateSort : function(tree, node){
13686         if(node.childrenRendered){
13687             this.doSort.defer(1, this, [node]);
13688         }
13689     }
13690 };/*
13691  * Based on:
13692  * Ext JS Library 1.1.1
13693  * Copyright(c) 2006-2007, Ext JS, LLC.
13694  *
13695  * Originally Released Under LGPL - original licence link has changed is not relivant.
13696  *
13697  * Fork - LGPL
13698  * <script type="text/javascript">
13699  */
13700
13701 if(Roo.dd.DropZone){
13702     
13703 Roo.tree.TreeDropZone = function(tree, config){
13704     this.allowParentInsert = false;
13705     this.allowContainerDrop = false;
13706     this.appendOnly = false;
13707     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13708     this.tree = tree;
13709     this.lastInsertClass = "x-tree-no-status";
13710     this.dragOverData = {};
13711 };
13712
13713 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13714     ddGroup : "TreeDD",
13715     scroll:  true,
13716     
13717     expandDelay : 1000,
13718     
13719     expandNode : function(node){
13720         if(node.hasChildNodes() && !node.isExpanded()){
13721             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13722         }
13723     },
13724     
13725     queueExpand : function(node){
13726         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13727     },
13728     
13729     cancelExpand : function(){
13730         if(this.expandProcId){
13731             clearTimeout(this.expandProcId);
13732             this.expandProcId = false;
13733         }
13734     },
13735     
13736     isValidDropPoint : function(n, pt, dd, e, data){
13737         if(!n || !data){ return false; }
13738         var targetNode = n.node;
13739         var dropNode = data.node;
13740         // default drop rules
13741         if(!(targetNode && targetNode.isTarget && pt)){
13742             return false;
13743         }
13744         if(pt == "append" && targetNode.allowChildren === false){
13745             return false;
13746         }
13747         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13748             return false;
13749         }
13750         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13751             return false;
13752         }
13753         // reuse the object
13754         var overEvent = this.dragOverData;
13755         overEvent.tree = this.tree;
13756         overEvent.target = targetNode;
13757         overEvent.data = data;
13758         overEvent.point = pt;
13759         overEvent.source = dd;
13760         overEvent.rawEvent = e;
13761         overEvent.dropNode = dropNode;
13762         overEvent.cancel = false;  
13763         var result = this.tree.fireEvent("nodedragover", overEvent);
13764         return overEvent.cancel === false && result !== false;
13765     },
13766     
13767     getDropPoint : function(e, n, dd)
13768     {
13769         var tn = n.node;
13770         if(tn.isRoot){
13771             return tn.allowChildren !== false ? "append" : false; // always append for root
13772         }
13773         var dragEl = n.ddel;
13774         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13775         var y = Roo.lib.Event.getPageY(e);
13776         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13777         
13778         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13779         var noAppend = tn.allowChildren === false;
13780         if(this.appendOnly || tn.parentNode.allowChildren === false){
13781             return noAppend ? false : "append";
13782         }
13783         var noBelow = false;
13784         if(!this.allowParentInsert){
13785             noBelow = tn.hasChildNodes() && tn.isExpanded();
13786         }
13787         var q = (b - t) / (noAppend ? 2 : 3);
13788         if(y >= t && y < (t + q)){
13789             return "above";
13790         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13791             return "below";
13792         }else{
13793             return "append";
13794         }
13795     },
13796     
13797     onNodeEnter : function(n, dd, e, data)
13798     {
13799         this.cancelExpand();
13800     },
13801     
13802     onNodeOver : function(n, dd, e, data)
13803     {
13804        
13805         var pt = this.getDropPoint(e, n, dd);
13806         var node = n.node;
13807         
13808         // auto node expand check
13809         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13810             this.queueExpand(node);
13811         }else if(pt != "append"){
13812             this.cancelExpand();
13813         }
13814         
13815         // set the insert point style on the target node
13816         var returnCls = this.dropNotAllowed;
13817         if(this.isValidDropPoint(n, pt, dd, e, data)){
13818            if(pt){
13819                var el = n.ddel;
13820                var cls;
13821                if(pt == "above"){
13822                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13823                    cls = "x-tree-drag-insert-above";
13824                }else if(pt == "below"){
13825                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13826                    cls = "x-tree-drag-insert-below";
13827                }else{
13828                    returnCls = "x-tree-drop-ok-append";
13829                    cls = "x-tree-drag-append";
13830                }
13831                if(this.lastInsertClass != cls){
13832                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13833                    this.lastInsertClass = cls;
13834                }
13835            }
13836        }
13837        return returnCls;
13838     },
13839     
13840     onNodeOut : function(n, dd, e, data){
13841         
13842         this.cancelExpand();
13843         this.removeDropIndicators(n);
13844     },
13845     
13846     onNodeDrop : function(n, dd, e, data){
13847         var point = this.getDropPoint(e, n, dd);
13848         var targetNode = n.node;
13849         targetNode.ui.startDrop();
13850         if(!this.isValidDropPoint(n, point, dd, e, data)){
13851             targetNode.ui.endDrop();
13852             return false;
13853         }
13854         // first try to find the drop node
13855         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13856         var dropEvent = {
13857             tree : this.tree,
13858             target: targetNode,
13859             data: data,
13860             point: point,
13861             source: dd,
13862             rawEvent: e,
13863             dropNode: dropNode,
13864             cancel: !dropNode   
13865         };
13866         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13867         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13868             targetNode.ui.endDrop();
13869             return false;
13870         }
13871         // allow target changing
13872         targetNode = dropEvent.target;
13873         if(point == "append" && !targetNode.isExpanded()){
13874             targetNode.expand(false, null, function(){
13875                 this.completeDrop(dropEvent);
13876             }.createDelegate(this));
13877         }else{
13878             this.completeDrop(dropEvent);
13879         }
13880         return true;
13881     },
13882     
13883     completeDrop : function(de){
13884         var ns = de.dropNode, p = de.point, t = de.target;
13885         if(!(ns instanceof Array)){
13886             ns = [ns];
13887         }
13888         var n;
13889         for(var i = 0, len = ns.length; i < len; i++){
13890             n = ns[i];
13891             if(p == "above"){
13892                 t.parentNode.insertBefore(n, t);
13893             }else if(p == "below"){
13894                 t.parentNode.insertBefore(n, t.nextSibling);
13895             }else{
13896                 t.appendChild(n);
13897             }
13898         }
13899         n.ui.focus();
13900         if(this.tree.hlDrop){
13901             n.ui.highlight();
13902         }
13903         t.ui.endDrop();
13904         this.tree.fireEvent("nodedrop", de);
13905     },
13906     
13907     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13908         if(this.tree.hlDrop){
13909             dropNode.ui.focus();
13910             dropNode.ui.highlight();
13911         }
13912         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13913     },
13914     
13915     getTree : function(){
13916         return this.tree;
13917     },
13918     
13919     removeDropIndicators : function(n){
13920         if(n && n.ddel){
13921             var el = n.ddel;
13922             Roo.fly(el).removeClass([
13923                     "x-tree-drag-insert-above",
13924                     "x-tree-drag-insert-below",
13925                     "x-tree-drag-append"]);
13926             this.lastInsertClass = "_noclass";
13927         }
13928     },
13929     
13930     beforeDragDrop : function(target, e, id){
13931         this.cancelExpand();
13932         return true;
13933     },
13934     
13935     afterRepair : function(data){
13936         if(data && Roo.enableFx){
13937             data.node.ui.highlight();
13938         }
13939         this.hideProxy();
13940     } 
13941     
13942 });
13943
13944 }
13945 /*
13946  * Based on:
13947  * Ext JS Library 1.1.1
13948  * Copyright(c) 2006-2007, Ext JS, LLC.
13949  *
13950  * Originally Released Under LGPL - original licence link has changed is not relivant.
13951  *
13952  * Fork - LGPL
13953  * <script type="text/javascript">
13954  */
13955  
13956
13957 if(Roo.dd.DragZone){
13958 Roo.tree.TreeDragZone = function(tree, config){
13959     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13960     this.tree = tree;
13961 };
13962
13963 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13964     ddGroup : "TreeDD",
13965    
13966     onBeforeDrag : function(data, e){
13967         var n = data.node;
13968         return n && n.draggable && !n.disabled;
13969     },
13970      
13971     
13972     onInitDrag : function(e){
13973         var data = this.dragData;
13974         this.tree.getSelectionModel().select(data.node);
13975         this.proxy.update("");
13976         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13977         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13978     },
13979     
13980     getRepairXY : function(e, data){
13981         return data.node.ui.getDDRepairXY();
13982     },
13983     
13984     onEndDrag : function(data, e){
13985         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13986         
13987         
13988     },
13989     
13990     onValidDrop : function(dd, e, id){
13991         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13992         this.hideProxy();
13993     },
13994     
13995     beforeInvalidDrop : function(e, id){
13996         // this scrolls the original position back into view
13997         var sm = this.tree.getSelectionModel();
13998         sm.clearSelections();
13999         sm.select(this.dragData.node);
14000     }
14001 });
14002 }/*
14003  * Based on:
14004  * Ext JS Library 1.1.1
14005  * Copyright(c) 2006-2007, Ext JS, LLC.
14006  *
14007  * Originally Released Under LGPL - original licence link has changed is not relivant.
14008  *
14009  * Fork - LGPL
14010  * <script type="text/javascript">
14011  */
14012 /**
14013  * @class Roo.tree.TreeEditor
14014  * @extends Roo.Editor
14015  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14016  * as the editor field.
14017  * @constructor
14018  * @param {Object} config (used to be the tree panel.)
14019  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14020  * 
14021  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14022  * @cfg {Roo.form.TextField} field [required] The field configuration
14023  *
14024  * 
14025  */
14026 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14027     var tree = config;
14028     var field;
14029     if (oldconfig) { // old style..
14030         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14031     } else {
14032         // new style..
14033         tree = config.tree;
14034         config.field = config.field  || {};
14035         config.field.xtype = 'TextField';
14036         field = Roo.factory(config.field, Roo.form);
14037     }
14038     config = config || {};
14039     
14040     
14041     this.addEvents({
14042         /**
14043          * @event beforenodeedit
14044          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14045          * false from the handler of this event.
14046          * @param {Editor} this
14047          * @param {Roo.tree.Node} node 
14048          */
14049         "beforenodeedit" : true
14050     });
14051     
14052     //Roo.log(config);
14053     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14054
14055     this.tree = tree;
14056
14057     tree.on('beforeclick', this.beforeNodeClick, this);
14058     tree.getTreeEl().on('mousedown', this.hide, this);
14059     this.on('complete', this.updateNode, this);
14060     this.on('beforestartedit', this.fitToTree, this);
14061     this.on('startedit', this.bindScroll, this, {delay:10});
14062     this.on('specialkey', this.onSpecialKey, this);
14063 };
14064
14065 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14066     /**
14067      * @cfg {String} alignment
14068      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14069      */
14070     alignment: "l-l",
14071     // inherit
14072     autoSize: false,
14073     /**
14074      * @cfg {Boolean} hideEl
14075      * True to hide the bound element while the editor is displayed (defaults to false)
14076      */
14077     hideEl : false,
14078     /**
14079      * @cfg {String} cls
14080      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14081      */
14082     cls: "x-small-editor x-tree-editor",
14083     /**
14084      * @cfg {Boolean} shim
14085      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14086      */
14087     shim:false,
14088     // inherit
14089     shadow:"frame",
14090     /**
14091      * @cfg {Number} maxWidth
14092      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14093      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14094      * scroll and client offsets into account prior to each edit.
14095      */
14096     maxWidth: 250,
14097
14098     editDelay : 350,
14099
14100     // private
14101     fitToTree : function(ed, el){
14102         var td = this.tree.getTreeEl().dom, nd = el.dom;
14103         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14104             td.scrollLeft = nd.offsetLeft;
14105         }
14106         var w = Math.min(
14107                 this.maxWidth,
14108                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14109         this.setSize(w, '');
14110         
14111         return this.fireEvent('beforenodeedit', this, this.editNode);
14112         
14113     },
14114
14115     // private
14116     triggerEdit : function(node){
14117         this.completeEdit();
14118         this.editNode = node;
14119         this.startEdit(node.ui.textNode, node.text);
14120     },
14121
14122     // private
14123     bindScroll : function(){
14124         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14125     },
14126
14127     // private
14128     beforeNodeClick : function(node, e){
14129         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14130         this.lastClick = new Date();
14131         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14132             e.stopEvent();
14133             this.triggerEdit(node);
14134             return false;
14135         }
14136         return true;
14137     },
14138
14139     // private
14140     updateNode : function(ed, value){
14141         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14142         this.editNode.setText(value);
14143     },
14144
14145     // private
14146     onHide : function(){
14147         Roo.tree.TreeEditor.superclass.onHide.call(this);
14148         if(this.editNode){
14149             this.editNode.ui.focus();
14150         }
14151     },
14152
14153     // private
14154     onSpecialKey : function(field, e){
14155         var k = e.getKey();
14156         if(k == e.ESC){
14157             e.stopEvent();
14158             this.cancelEdit();
14159         }else if(k == e.ENTER && !e.hasModifier()){
14160             e.stopEvent();
14161             this.completeEdit();
14162         }
14163     }
14164 });//<Script type="text/javascript">
14165 /*
14166  * Based on:
14167  * Ext JS Library 1.1.1
14168  * Copyright(c) 2006-2007, Ext JS, LLC.
14169  *
14170  * Originally Released Under LGPL - original licence link has changed is not relivant.
14171  *
14172  * Fork - LGPL
14173  * <script type="text/javascript">
14174  */
14175  
14176 /**
14177  * Not documented??? - probably should be...
14178  */
14179
14180 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14181     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14182     
14183     renderElements : function(n, a, targetNode, bulkRender){
14184         //consel.log("renderElements?");
14185         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14186
14187         var t = n.getOwnerTree();
14188         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14189         
14190         var cols = t.columns;
14191         var bw = t.borderWidth;
14192         var c = cols[0];
14193         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14194          var cb = typeof a.checked == "boolean";
14195         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14196         var colcls = 'x-t-' + tid + '-c0';
14197         var buf = [
14198             '<li class="x-tree-node">',
14199             
14200                 
14201                 '<div class="x-tree-node-el ', a.cls,'">',
14202                     // extran...
14203                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14204                 
14205                 
14206                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14207                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14208                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14209                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14210                            (a.iconCls ? ' '+a.iconCls : ''),
14211                            '" unselectable="on" />',
14212                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14213                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14214                              
14215                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14216                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14217                             '<span unselectable="on" qtip="' + tx + '">',
14218                              tx,
14219                              '</span></a>' ,
14220                     '</div>',
14221                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14222                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14223                  ];
14224         for(var i = 1, len = cols.length; i < len; i++){
14225             c = cols[i];
14226             colcls = 'x-t-' + tid + '-c' +i;
14227             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14228             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14229                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14230                       "</div>");
14231          }
14232          
14233          buf.push(
14234             '</a>',
14235             '<div class="x-clear"></div></div>',
14236             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14237             "</li>");
14238         
14239         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14240             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14241                                 n.nextSibling.ui.getEl(), buf.join(""));
14242         }else{
14243             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14244         }
14245         var el = this.wrap.firstChild;
14246         this.elRow = el;
14247         this.elNode = el.firstChild;
14248         this.ranchor = el.childNodes[1];
14249         this.ctNode = this.wrap.childNodes[1];
14250         var cs = el.firstChild.childNodes;
14251         this.indentNode = cs[0];
14252         this.ecNode = cs[1];
14253         this.iconNode = cs[2];
14254         var index = 3;
14255         if(cb){
14256             this.checkbox = cs[3];
14257             index++;
14258         }
14259         this.anchor = cs[index];
14260         
14261         this.textNode = cs[index].firstChild;
14262         
14263         //el.on("click", this.onClick, this);
14264         //el.on("dblclick", this.onDblClick, this);
14265         
14266         
14267        // console.log(this);
14268     },
14269     initEvents : function(){
14270         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14271         
14272             
14273         var a = this.ranchor;
14274
14275         var el = Roo.get(a);
14276
14277         if(Roo.isOpera){ // opera render bug ignores the CSS
14278             el.setStyle("text-decoration", "none");
14279         }
14280
14281         el.on("click", this.onClick, this);
14282         el.on("dblclick", this.onDblClick, this);
14283         el.on("contextmenu", this.onContextMenu, this);
14284         
14285     },
14286     
14287     /*onSelectedChange : function(state){
14288         if(state){
14289             this.focus();
14290             this.addClass("x-tree-selected");
14291         }else{
14292             //this.blur();
14293             this.removeClass("x-tree-selected");
14294         }
14295     },*/
14296     addClass : function(cls){
14297         if(this.elRow){
14298             Roo.fly(this.elRow).addClass(cls);
14299         }
14300         
14301     },
14302     
14303     
14304     removeClass : function(cls){
14305         if(this.elRow){
14306             Roo.fly(this.elRow).removeClass(cls);
14307         }
14308     }
14309
14310     
14311     
14312 });//<Script type="text/javascript">
14313
14314 /*
14315  * Based on:
14316  * Ext JS Library 1.1.1
14317  * Copyright(c) 2006-2007, Ext JS, LLC.
14318  *
14319  * Originally Released Under LGPL - original licence link has changed is not relivant.
14320  *
14321  * Fork - LGPL
14322  * <script type="text/javascript">
14323  */
14324  
14325
14326 /**
14327  * @class Roo.tree.ColumnTree
14328  * @extends Roo.tree.TreePanel
14329  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14330  * @cfg {int} borderWidth  compined right/left border allowance
14331  * @constructor
14332  * @param {String/HTMLElement/Element} el The container element
14333  * @param {Object} config
14334  */
14335 Roo.tree.ColumnTree =  function(el, config)
14336 {
14337    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14338    this.addEvents({
14339         /**
14340         * @event resize
14341         * Fire this event on a container when it resizes
14342         * @param {int} w Width
14343         * @param {int} h Height
14344         */
14345        "resize" : true
14346     });
14347     this.on('resize', this.onResize, this);
14348 };
14349
14350 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14351     //lines:false,
14352     
14353     
14354     borderWidth: Roo.isBorderBox ? 0 : 2, 
14355     headEls : false,
14356     
14357     render : function(){
14358         // add the header.....
14359        
14360         Roo.tree.ColumnTree.superclass.render.apply(this);
14361         
14362         this.el.addClass('x-column-tree');
14363         
14364         this.headers = this.el.createChild(
14365             {cls:'x-tree-headers'},this.innerCt.dom);
14366    
14367         var cols = this.columns, c;
14368         var totalWidth = 0;
14369         this.headEls = [];
14370         var  len = cols.length;
14371         for(var i = 0; i < len; i++){
14372              c = cols[i];
14373              totalWidth += c.width;
14374             this.headEls.push(this.headers.createChild({
14375                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14376                  cn: {
14377                      cls:'x-tree-hd-text',
14378                      html: c.header
14379                  },
14380                  style:'width:'+(c.width-this.borderWidth)+'px;'
14381              }));
14382         }
14383         this.headers.createChild({cls:'x-clear'});
14384         // prevent floats from wrapping when clipped
14385         this.headers.setWidth(totalWidth);
14386         //this.innerCt.setWidth(totalWidth);
14387         this.innerCt.setStyle({ overflow: 'auto' });
14388         this.onResize(this.width, this.height);
14389              
14390         
14391     },
14392     onResize : function(w,h)
14393     {
14394         this.height = h;
14395         this.width = w;
14396         // resize cols..
14397         this.innerCt.setWidth(this.width);
14398         this.innerCt.setHeight(this.height-20);
14399         
14400         // headers...
14401         var cols = this.columns, c;
14402         var totalWidth = 0;
14403         var expEl = false;
14404         var len = cols.length;
14405         for(var i = 0; i < len; i++){
14406             c = cols[i];
14407             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14408                 // it's the expander..
14409                 expEl  = this.headEls[i];
14410                 continue;
14411             }
14412             totalWidth += c.width;
14413             
14414         }
14415         if (expEl) {
14416             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14417         }
14418         this.headers.setWidth(w-20);
14419
14420         
14421         
14422         
14423     }
14424 });
14425 /*
14426  * Based on:
14427  * Ext JS Library 1.1.1
14428  * Copyright(c) 2006-2007, Ext JS, LLC.
14429  *
14430  * Originally Released Under LGPL - original licence link has changed is not relivant.
14431  *
14432  * Fork - LGPL
14433  * <script type="text/javascript">
14434  */
14435  
14436 /**
14437  * @class Roo.menu.Menu
14438  * @extends Roo.util.Observable
14439  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14440  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14441  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14442  * @constructor
14443  * Creates a new Menu
14444  * @param {Object} config Configuration options
14445  */
14446 Roo.menu.Menu = function(config){
14447     
14448     Roo.menu.Menu.superclass.constructor.call(this, config);
14449     
14450     this.id = this.id || Roo.id();
14451     this.addEvents({
14452         /**
14453          * @event beforeshow
14454          * Fires before this menu is displayed
14455          * @param {Roo.menu.Menu} this
14456          */
14457         beforeshow : true,
14458         /**
14459          * @event beforehide
14460          * Fires before this menu is hidden
14461          * @param {Roo.menu.Menu} this
14462          */
14463         beforehide : true,
14464         /**
14465          * @event show
14466          * Fires after this menu is displayed
14467          * @param {Roo.menu.Menu} this
14468          */
14469         show : true,
14470         /**
14471          * @event hide
14472          * Fires after this menu is hidden
14473          * @param {Roo.menu.Menu} this
14474          */
14475         hide : true,
14476         /**
14477          * @event click
14478          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14479          * @param {Roo.menu.Menu} this
14480          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14481          * @param {Roo.EventObject} e
14482          */
14483         click : true,
14484         /**
14485          * @event mouseover
14486          * Fires when the mouse is hovering over this menu
14487          * @param {Roo.menu.Menu} this
14488          * @param {Roo.EventObject} e
14489          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14490          */
14491         mouseover : true,
14492         /**
14493          * @event mouseout
14494          * Fires when the mouse exits this menu
14495          * @param {Roo.menu.Menu} this
14496          * @param {Roo.EventObject} e
14497          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14498          */
14499         mouseout : true,
14500         /**
14501          * @event itemclick
14502          * Fires when a menu item contained in this menu is clicked
14503          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14504          * @param {Roo.EventObject} e
14505          */
14506         itemclick: true
14507     });
14508     if (this.registerMenu) {
14509         Roo.menu.MenuMgr.register(this);
14510     }
14511     
14512     var mis = this.items;
14513     this.items = new Roo.util.MixedCollection();
14514     if(mis){
14515         this.add.apply(this, mis);
14516     }
14517 };
14518
14519 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14520     /**
14521      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14522      */
14523     minWidth : 120,
14524     /**
14525      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14526      * for bottom-right shadow (defaults to "sides")
14527      */
14528     shadow : "sides",
14529     /**
14530      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14531      * this menu (defaults to "tl-tr?")
14532      */
14533     subMenuAlign : "tl-tr?",
14534     /**
14535      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14536      * relative to its element of origin (defaults to "tl-bl?")
14537      */
14538     defaultAlign : "tl-bl?",
14539     /**
14540      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14541      */
14542     allowOtherMenus : false,
14543     /**
14544      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14545      */
14546     registerMenu : true,
14547
14548     hidden:true,
14549
14550     // private
14551     render : function(){
14552         if(this.el){
14553             return;
14554         }
14555         var el = this.el = new Roo.Layer({
14556             cls: "x-menu",
14557             shadow:this.shadow,
14558             constrain: false,
14559             parentEl: this.parentEl || document.body,
14560             zindex:15000
14561         });
14562
14563         this.keyNav = new Roo.menu.MenuNav(this);
14564
14565         if(this.plain){
14566             el.addClass("x-menu-plain");
14567         }
14568         if(this.cls){
14569             el.addClass(this.cls);
14570         }
14571         // generic focus element
14572         this.focusEl = el.createChild({
14573             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14574         });
14575         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14576         //disabling touch- as it's causing issues ..
14577         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14578         ul.on('click'   , this.onClick, this);
14579         
14580         
14581         ul.on("mouseover", this.onMouseOver, this);
14582         ul.on("mouseout", this.onMouseOut, this);
14583         this.items.each(function(item){
14584             if (item.hidden) {
14585                 return;
14586             }
14587             
14588             var li = document.createElement("li");
14589             li.className = "x-menu-list-item";
14590             ul.dom.appendChild(li);
14591             item.render(li, this);
14592         }, this);
14593         this.ul = ul;
14594         this.autoWidth();
14595     },
14596
14597     // private
14598     autoWidth : function(){
14599         var el = this.el, ul = this.ul;
14600         if(!el){
14601             return;
14602         }
14603         var w = this.width;
14604         if(w){
14605             el.setWidth(w);
14606         }else if(Roo.isIE){
14607             el.setWidth(this.minWidth);
14608             var t = el.dom.offsetWidth; // force recalc
14609             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14610         }
14611     },
14612
14613     // private
14614     delayAutoWidth : function(){
14615         if(this.rendered){
14616             if(!this.awTask){
14617                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14618             }
14619             this.awTask.delay(20);
14620         }
14621     },
14622
14623     // private
14624     findTargetItem : function(e){
14625         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14626         if(t && t.menuItemId){
14627             return this.items.get(t.menuItemId);
14628         }
14629     },
14630
14631     // private
14632     onClick : function(e){
14633         Roo.log("menu.onClick");
14634         var t = this.findTargetItem(e);
14635         if(!t){
14636             return;
14637         }
14638         Roo.log(e);
14639         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14640             if(t == this.activeItem && t.shouldDeactivate(e)){
14641                 this.activeItem.deactivate();
14642                 delete this.activeItem;
14643                 return;
14644             }
14645             if(t.canActivate){
14646                 this.setActiveItem(t, true);
14647             }
14648             return;
14649             
14650             
14651         }
14652         
14653         t.onClick(e);
14654         this.fireEvent("click", this, t, e);
14655     },
14656
14657     // private
14658     setActiveItem : function(item, autoExpand){
14659         if(item != this.activeItem){
14660             if(this.activeItem){
14661                 this.activeItem.deactivate();
14662             }
14663             this.activeItem = item;
14664             item.activate(autoExpand);
14665         }else if(autoExpand){
14666             item.expandMenu();
14667         }
14668     },
14669
14670     // private
14671     tryActivate : function(start, step){
14672         var items = this.items;
14673         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14674             var item = items.get(i);
14675             if(!item.disabled && item.canActivate){
14676                 this.setActiveItem(item, false);
14677                 return item;
14678             }
14679         }
14680         return false;
14681     },
14682
14683     // private
14684     onMouseOver : function(e){
14685         var t;
14686         if(t = this.findTargetItem(e)){
14687             if(t.canActivate && !t.disabled){
14688                 this.setActiveItem(t, true);
14689             }
14690         }
14691         this.fireEvent("mouseover", this, e, t);
14692     },
14693
14694     // private
14695     onMouseOut : function(e){
14696         var t;
14697         if(t = this.findTargetItem(e)){
14698             if(t == this.activeItem && t.shouldDeactivate(e)){
14699                 this.activeItem.deactivate();
14700                 delete this.activeItem;
14701             }
14702         }
14703         this.fireEvent("mouseout", this, e, t);
14704     },
14705
14706     /**
14707      * Read-only.  Returns true if the menu is currently displayed, else false.
14708      * @type Boolean
14709      */
14710     isVisible : function(){
14711         return this.el && !this.hidden;
14712     },
14713
14714     /**
14715      * Displays this menu relative to another element
14716      * @param {String/HTMLElement/Roo.Element} element The element to align to
14717      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14718      * the element (defaults to this.defaultAlign)
14719      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14720      */
14721     show : function(el, pos, parentMenu){
14722         this.parentMenu = parentMenu;
14723         if(!this.el){
14724             this.render();
14725         }
14726         this.fireEvent("beforeshow", this);
14727         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14728     },
14729
14730     /**
14731      * Displays this menu at a specific xy position
14732      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14733      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14734      */
14735     showAt : function(xy, parentMenu, /* private: */_e){
14736         this.parentMenu = parentMenu;
14737         if(!this.el){
14738             this.render();
14739         }
14740         if(_e !== false){
14741             this.fireEvent("beforeshow", this);
14742             xy = this.el.adjustForConstraints(xy);
14743         }
14744         this.el.setXY(xy);
14745         this.el.show();
14746         this.hidden = false;
14747         this.focus();
14748         this.fireEvent("show", this);
14749     },
14750
14751     focus : function(){
14752         if(!this.hidden){
14753             this.doFocus.defer(50, this);
14754         }
14755     },
14756
14757     doFocus : function(){
14758         if(!this.hidden){
14759             this.focusEl.focus();
14760         }
14761     },
14762
14763     /**
14764      * Hides this menu and optionally all parent menus
14765      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14766      */
14767     hide : function(deep){
14768         if(this.el && this.isVisible()){
14769             this.fireEvent("beforehide", this);
14770             if(this.activeItem){
14771                 this.activeItem.deactivate();
14772                 this.activeItem = null;
14773             }
14774             this.el.hide();
14775             this.hidden = true;
14776             this.fireEvent("hide", this);
14777         }
14778         if(deep === true && this.parentMenu){
14779             this.parentMenu.hide(true);
14780         }
14781     },
14782
14783     /**
14784      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14785      * Any of the following are valid:
14786      * <ul>
14787      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14788      * <li>An HTMLElement object which will be converted to a menu item</li>
14789      * <li>A menu item config object that will be created as a new menu item</li>
14790      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14791      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14792      * </ul>
14793      * Usage:
14794      * <pre><code>
14795 // Create the menu
14796 var menu = new Roo.menu.Menu();
14797
14798 // Create a menu item to add by reference
14799 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14800
14801 // Add a bunch of items at once using different methods.
14802 // Only the last item added will be returned.
14803 var item = menu.add(
14804     menuItem,                // add existing item by ref
14805     'Dynamic Item',          // new TextItem
14806     '-',                     // new separator
14807     { text: 'Config Item' }  // new item by config
14808 );
14809 </code></pre>
14810      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14811      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14812      */
14813     add : function(){
14814         var a = arguments, l = a.length, item;
14815         for(var i = 0; i < l; i++){
14816             var el = a[i];
14817             if ((typeof(el) == "object") && el.xtype && el.xns) {
14818                 el = Roo.factory(el, Roo.menu);
14819             }
14820             
14821             if(el.render){ // some kind of Item
14822                 item = this.addItem(el);
14823             }else if(typeof el == "string"){ // string
14824                 if(el == "separator" || el == "-"){
14825                     item = this.addSeparator();
14826                 }else{
14827                     item = this.addText(el);
14828                 }
14829             }else if(el.tagName || el.el){ // element
14830                 item = this.addElement(el);
14831             }else if(typeof el == "object"){ // must be menu item config?
14832                 item = this.addMenuItem(el);
14833             }
14834         }
14835         return item;
14836     },
14837
14838     /**
14839      * Returns this menu's underlying {@link Roo.Element} object
14840      * @return {Roo.Element} The element
14841      */
14842     getEl : function(){
14843         if(!this.el){
14844             this.render();
14845         }
14846         return this.el;
14847     },
14848
14849     /**
14850      * Adds a separator bar to the menu
14851      * @return {Roo.menu.Item} The menu item that was added
14852      */
14853     addSeparator : function(){
14854         return this.addItem(new Roo.menu.Separator());
14855     },
14856
14857     /**
14858      * Adds an {@link Roo.Element} object to the menu
14859      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14860      * @return {Roo.menu.Item} The menu item that was added
14861      */
14862     addElement : function(el){
14863         return this.addItem(new Roo.menu.BaseItem(el));
14864     },
14865
14866     /**
14867      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14868      * @param {Roo.menu.Item} item The menu item to add
14869      * @return {Roo.menu.Item} The menu item that was added
14870      */
14871     addItem : function(item){
14872         this.items.add(item);
14873         if(this.ul){
14874             var li = document.createElement("li");
14875             li.className = "x-menu-list-item";
14876             this.ul.dom.appendChild(li);
14877             item.render(li, this);
14878             this.delayAutoWidth();
14879         }
14880         return item;
14881     },
14882
14883     /**
14884      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14885      * @param {Object} config A MenuItem config object
14886      * @return {Roo.menu.Item} The menu item that was added
14887      */
14888     addMenuItem : function(config){
14889         if(!(config instanceof Roo.menu.Item)){
14890             if(typeof config.checked == "boolean"){ // must be check menu item config?
14891                 config = new Roo.menu.CheckItem(config);
14892             }else{
14893                 config = new Roo.menu.Item(config);
14894             }
14895         }
14896         return this.addItem(config);
14897     },
14898
14899     /**
14900      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14901      * @param {String} text The text to display in the menu item
14902      * @return {Roo.menu.Item} The menu item that was added
14903      */
14904     addText : function(text){
14905         return this.addItem(new Roo.menu.TextItem({ text : text }));
14906     },
14907
14908     /**
14909      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14910      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14911      * @param {Roo.menu.Item} item The menu item to add
14912      * @return {Roo.menu.Item} The menu item that was added
14913      */
14914     insert : function(index, item){
14915         this.items.insert(index, item);
14916         if(this.ul){
14917             var li = document.createElement("li");
14918             li.className = "x-menu-list-item";
14919             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14920             item.render(li, this);
14921             this.delayAutoWidth();
14922         }
14923         return item;
14924     },
14925
14926     /**
14927      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14928      * @param {Roo.menu.Item} item The menu item to remove
14929      */
14930     remove : function(item){
14931         this.items.removeKey(item.id);
14932         item.destroy();
14933     },
14934
14935     /**
14936      * Removes and destroys all items in the menu
14937      */
14938     removeAll : function(){
14939         var f;
14940         while(f = this.items.first()){
14941             this.remove(f);
14942         }
14943     }
14944 });
14945
14946 // MenuNav is a private utility class used internally by the Menu
14947 Roo.menu.MenuNav = function(menu){
14948     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14949     this.scope = this.menu = menu;
14950 };
14951
14952 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14953     doRelay : function(e, h){
14954         var k = e.getKey();
14955         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14956             this.menu.tryActivate(0, 1);
14957             return false;
14958         }
14959         return h.call(this.scope || this, e, this.menu);
14960     },
14961
14962     up : function(e, m){
14963         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14964             m.tryActivate(m.items.length-1, -1);
14965         }
14966     },
14967
14968     down : function(e, m){
14969         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14970             m.tryActivate(0, 1);
14971         }
14972     },
14973
14974     right : function(e, m){
14975         if(m.activeItem){
14976             m.activeItem.expandMenu(true);
14977         }
14978     },
14979
14980     left : function(e, m){
14981         m.hide();
14982         if(m.parentMenu && m.parentMenu.activeItem){
14983             m.parentMenu.activeItem.activate();
14984         }
14985     },
14986
14987     enter : function(e, m){
14988         if(m.activeItem){
14989             e.stopPropagation();
14990             m.activeItem.onClick(e);
14991             m.fireEvent("click", this, m.activeItem);
14992             return true;
14993         }
14994     }
14995 });/*
14996  * Based on:
14997  * Ext JS Library 1.1.1
14998  * Copyright(c) 2006-2007, Ext JS, LLC.
14999  *
15000  * Originally Released Under LGPL - original licence link has changed is not relivant.
15001  *
15002  * Fork - LGPL
15003  * <script type="text/javascript">
15004  */
15005  
15006 /**
15007  * @class Roo.menu.MenuMgr
15008  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15009  * @static
15010  */
15011 Roo.menu.MenuMgr = function(){
15012    var menus, active, groups = {}, attached = false, lastShow = new Date();
15013
15014    // private - called when first menu is created
15015    function init(){
15016        menus = {};
15017        active = new Roo.util.MixedCollection();
15018        Roo.get(document).addKeyListener(27, function(){
15019            if(active.length > 0){
15020                hideAll();
15021            }
15022        });
15023    }
15024
15025    // private
15026    function hideAll(){
15027        if(active && active.length > 0){
15028            var c = active.clone();
15029            c.each(function(m){
15030                m.hide();
15031            });
15032        }
15033    }
15034
15035    // private
15036    function onHide(m){
15037        active.remove(m);
15038        if(active.length < 1){
15039            Roo.get(document).un("mousedown", onMouseDown);
15040            attached = false;
15041        }
15042    }
15043
15044    // private
15045    function onShow(m){
15046        var last = active.last();
15047        lastShow = new Date();
15048        active.add(m);
15049        if(!attached){
15050            Roo.get(document).on("mousedown", onMouseDown);
15051            attached = true;
15052        }
15053        if(m.parentMenu){
15054           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15055           m.parentMenu.activeChild = m;
15056        }else if(last && last.isVisible()){
15057           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15058        }
15059    }
15060
15061    // private
15062    function onBeforeHide(m){
15063        if(m.activeChild){
15064            m.activeChild.hide();
15065        }
15066        if(m.autoHideTimer){
15067            clearTimeout(m.autoHideTimer);
15068            delete m.autoHideTimer;
15069        }
15070    }
15071
15072    // private
15073    function onBeforeShow(m){
15074        var pm = m.parentMenu;
15075        if(!pm && !m.allowOtherMenus){
15076            hideAll();
15077        }else if(pm && pm.activeChild && active != m){
15078            pm.activeChild.hide();
15079        }
15080    }
15081
15082    // private
15083    function onMouseDown(e){
15084        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15085            hideAll();
15086        }
15087    }
15088
15089    // private
15090    function onBeforeCheck(mi, state){
15091        if(state){
15092            var g = groups[mi.group];
15093            for(var i = 0, l = g.length; i < l; i++){
15094                if(g[i] != mi){
15095                    g[i].setChecked(false);
15096                }
15097            }
15098        }
15099    }
15100
15101    return {
15102
15103        /**
15104         * Hides all menus that are currently visible
15105         */
15106        hideAll : function(){
15107             hideAll();  
15108        },
15109
15110        // private
15111        register : function(menu){
15112            if(!menus){
15113                init();
15114            }
15115            menus[menu.id] = menu;
15116            menu.on("beforehide", onBeforeHide);
15117            menu.on("hide", onHide);
15118            menu.on("beforeshow", onBeforeShow);
15119            menu.on("show", onShow);
15120            var g = menu.group;
15121            if(g && menu.events["checkchange"]){
15122                if(!groups[g]){
15123                    groups[g] = [];
15124                }
15125                groups[g].push(menu);
15126                menu.on("checkchange", onCheck);
15127            }
15128        },
15129
15130         /**
15131          * Returns a {@link Roo.menu.Menu} object
15132          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15133          * be used to generate and return a new Menu instance.
15134          */
15135        get : function(menu){
15136            if(typeof menu == "string"){ // menu id
15137                return menus[menu];
15138            }else if(menu.events){  // menu instance
15139                return menu;
15140            }else if(typeof menu.length == 'number'){ // array of menu items?
15141                return new Roo.menu.Menu({items:menu});
15142            }else{ // otherwise, must be a config
15143                return new Roo.menu.Menu(menu);
15144            }
15145        },
15146
15147        // private
15148        unregister : function(menu){
15149            delete menus[menu.id];
15150            menu.un("beforehide", onBeforeHide);
15151            menu.un("hide", onHide);
15152            menu.un("beforeshow", onBeforeShow);
15153            menu.un("show", onShow);
15154            var g = menu.group;
15155            if(g && menu.events["checkchange"]){
15156                groups[g].remove(menu);
15157                menu.un("checkchange", onCheck);
15158            }
15159        },
15160
15161        // private
15162        registerCheckable : function(menuItem){
15163            var g = menuItem.group;
15164            if(g){
15165                if(!groups[g]){
15166                    groups[g] = [];
15167                }
15168                groups[g].push(menuItem);
15169                menuItem.on("beforecheckchange", onBeforeCheck);
15170            }
15171        },
15172
15173        // private
15174        unregisterCheckable : function(menuItem){
15175            var g = menuItem.group;
15176            if(g){
15177                groups[g].remove(menuItem);
15178                menuItem.un("beforecheckchange", onBeforeCheck);
15179            }
15180        }
15181    };
15182 }();/*
15183  * Based on:
15184  * Ext JS Library 1.1.1
15185  * Copyright(c) 2006-2007, Ext JS, LLC.
15186  *
15187  * Originally Released Under LGPL - original licence link has changed is not relivant.
15188  *
15189  * Fork - LGPL
15190  * <script type="text/javascript">
15191  */
15192  
15193
15194 /**
15195  * @class Roo.menu.BaseItem
15196  * @extends Roo.Component
15197  * @abstract
15198  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15199  * management and base configuration options shared by all menu components.
15200  * @constructor
15201  * Creates a new BaseItem
15202  * @param {Object} config Configuration options
15203  */
15204 Roo.menu.BaseItem = function(config){
15205     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15206
15207     this.addEvents({
15208         /**
15209          * @event click
15210          * Fires when this item is clicked
15211          * @param {Roo.menu.BaseItem} this
15212          * @param {Roo.EventObject} e
15213          */
15214         click: true,
15215         /**
15216          * @event activate
15217          * Fires when this item is activated
15218          * @param {Roo.menu.BaseItem} this
15219          */
15220         activate : true,
15221         /**
15222          * @event deactivate
15223          * Fires when this item is deactivated
15224          * @param {Roo.menu.BaseItem} this
15225          */
15226         deactivate : true
15227     });
15228
15229     if(this.handler){
15230         this.on("click", this.handler, this.scope, true);
15231     }
15232 };
15233
15234 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15235     /**
15236      * @cfg {Function} handler
15237      * A function that will handle the click event of this menu item (defaults to undefined)
15238      */
15239     /**
15240      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15241      */
15242     canActivate : false,
15243     
15244      /**
15245      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15246      */
15247     hidden: false,
15248     
15249     /**
15250      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15251      */
15252     activeClass : "x-menu-item-active",
15253     /**
15254      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15255      */
15256     hideOnClick : true,
15257     /**
15258      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15259      */
15260     hideDelay : 100,
15261
15262     // private
15263     ctype: "Roo.menu.BaseItem",
15264
15265     // private
15266     actionMode : "container",
15267
15268     // private
15269     render : function(container, parentMenu){
15270         this.parentMenu = parentMenu;
15271         Roo.menu.BaseItem.superclass.render.call(this, container);
15272         this.container.menuItemId = this.id;
15273     },
15274
15275     // private
15276     onRender : function(container, position){
15277         this.el = Roo.get(this.el);
15278         container.dom.appendChild(this.el.dom);
15279     },
15280
15281     // private
15282     onClick : function(e){
15283         if(!this.disabled && this.fireEvent("click", this, e) !== false
15284                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15285             this.handleClick(e);
15286         }else{
15287             e.stopEvent();
15288         }
15289     },
15290
15291     // private
15292     activate : function(){
15293         if(this.disabled){
15294             return false;
15295         }
15296         var li = this.container;
15297         li.addClass(this.activeClass);
15298         this.region = li.getRegion().adjust(2, 2, -2, -2);
15299         this.fireEvent("activate", this);
15300         return true;
15301     },
15302
15303     // private
15304     deactivate : function(){
15305         this.container.removeClass(this.activeClass);
15306         this.fireEvent("deactivate", this);
15307     },
15308
15309     // private
15310     shouldDeactivate : function(e){
15311         return !this.region || !this.region.contains(e.getPoint());
15312     },
15313
15314     // private
15315     handleClick : function(e){
15316         if(this.hideOnClick){
15317             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15318         }
15319     },
15320
15321     // private
15322     expandMenu : function(autoActivate){
15323         // do nothing
15324     },
15325
15326     // private
15327     hideMenu : function(){
15328         // do nothing
15329     }
15330 });/*
15331  * Based on:
15332  * Ext JS Library 1.1.1
15333  * Copyright(c) 2006-2007, Ext JS, LLC.
15334  *
15335  * Originally Released Under LGPL - original licence link has changed is not relivant.
15336  *
15337  * Fork - LGPL
15338  * <script type="text/javascript">
15339  */
15340  
15341 /**
15342  * @class Roo.menu.Adapter
15343  * @extends Roo.menu.BaseItem
15344  * @abstract
15345  * 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.
15346  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15347  * @constructor
15348  * Creates a new Adapter
15349  * @param {Object} config Configuration options
15350  */
15351 Roo.menu.Adapter = function(component, config){
15352     Roo.menu.Adapter.superclass.constructor.call(this, config);
15353     this.component = component;
15354 };
15355 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15356     // private
15357     canActivate : true,
15358
15359     // private
15360     onRender : function(container, position){
15361         this.component.render(container);
15362         this.el = this.component.getEl();
15363     },
15364
15365     // private
15366     activate : function(){
15367         if(this.disabled){
15368             return false;
15369         }
15370         this.component.focus();
15371         this.fireEvent("activate", this);
15372         return true;
15373     },
15374
15375     // private
15376     deactivate : function(){
15377         this.fireEvent("deactivate", this);
15378     },
15379
15380     // private
15381     disable : function(){
15382         this.component.disable();
15383         Roo.menu.Adapter.superclass.disable.call(this);
15384     },
15385
15386     // private
15387     enable : function(){
15388         this.component.enable();
15389         Roo.menu.Adapter.superclass.enable.call(this);
15390     }
15391 });/*
15392  * Based on:
15393  * Ext JS Library 1.1.1
15394  * Copyright(c) 2006-2007, Ext JS, LLC.
15395  *
15396  * Originally Released Under LGPL - original licence link has changed is not relivant.
15397  *
15398  * Fork - LGPL
15399  * <script type="text/javascript">
15400  */
15401
15402 /**
15403  * @class Roo.menu.TextItem
15404  * @extends Roo.menu.BaseItem
15405  * Adds a static text string to a menu, usually used as either a heading or group separator.
15406  * Note: old style constructor with text is still supported.
15407  * 
15408  * @constructor
15409  * Creates a new TextItem
15410  * @param {Object} cfg Configuration
15411  */
15412 Roo.menu.TextItem = function(cfg){
15413     if (typeof(cfg) == 'string') {
15414         this.text = cfg;
15415     } else {
15416         Roo.apply(this,cfg);
15417     }
15418     
15419     Roo.menu.TextItem.superclass.constructor.call(this);
15420 };
15421
15422 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15423     /**
15424      * @cfg {String} text Text to show on item.
15425      */
15426     text : '',
15427     
15428     /**
15429      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15430      */
15431     hideOnClick : false,
15432     /**
15433      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15434      */
15435     itemCls : "x-menu-text",
15436
15437     // private
15438     onRender : function(){
15439         var s = document.createElement("span");
15440         s.className = this.itemCls;
15441         s.innerHTML = this.text;
15442         this.el = s;
15443         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15444     }
15445 });/*
15446  * Based on:
15447  * Ext JS Library 1.1.1
15448  * Copyright(c) 2006-2007, Ext JS, LLC.
15449  *
15450  * Originally Released Under LGPL - original licence link has changed is not relivant.
15451  *
15452  * Fork - LGPL
15453  * <script type="text/javascript">
15454  */
15455
15456 /**
15457  * @class Roo.menu.Separator
15458  * @extends Roo.menu.BaseItem
15459  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15460  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15461  * @constructor
15462  * @param {Object} config Configuration options
15463  */
15464 Roo.menu.Separator = function(config){
15465     Roo.menu.Separator.superclass.constructor.call(this, config);
15466 };
15467
15468 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15469     /**
15470      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15471      */
15472     itemCls : "x-menu-sep",
15473     /**
15474      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15475      */
15476     hideOnClick : false,
15477
15478     // private
15479     onRender : function(li){
15480         var s = document.createElement("span");
15481         s.className = this.itemCls;
15482         s.innerHTML = "&#160;";
15483         this.el = s;
15484         li.addClass("x-menu-sep-li");
15485         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15486     }
15487 });/*
15488  * Based on:
15489  * Ext JS Library 1.1.1
15490  * Copyright(c) 2006-2007, Ext JS, LLC.
15491  *
15492  * Originally Released Under LGPL - original licence link has changed is not relivant.
15493  *
15494  * Fork - LGPL
15495  * <script type="text/javascript">
15496  */
15497 /**
15498  * @class Roo.menu.Item
15499  * @extends Roo.menu.BaseItem
15500  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15501  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15502  * activation and click handling.
15503  * @constructor
15504  * Creates a new Item
15505  * @param {Object} config Configuration options
15506  */
15507 Roo.menu.Item = function(config){
15508     Roo.menu.Item.superclass.constructor.call(this, config);
15509     if(this.menu){
15510         this.menu = Roo.menu.MenuMgr.get(this.menu);
15511     }
15512 };
15513 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15514     /**
15515      * @cfg {Roo.menu.Menu} menu
15516      * A Sub menu
15517      */
15518     /**
15519      * @cfg {String} text
15520      * The text to show on the menu item.
15521      */
15522     text: '',
15523      /**
15524      * @cfg {String} html to render in menu
15525      * The text to show on the menu item (HTML version).
15526      */
15527     html: '',
15528     /**
15529      * @cfg {String} icon
15530      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15531      */
15532     icon: undefined,
15533     /**
15534      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15535      */
15536     itemCls : "x-menu-item",
15537     /**
15538      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15539      */
15540     canActivate : true,
15541     /**
15542      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15543      */
15544     showDelay: 200,
15545     // doc'd in BaseItem
15546     hideDelay: 200,
15547
15548     // private
15549     ctype: "Roo.menu.Item",
15550     
15551     // private
15552     onRender : function(container, position){
15553         var el = document.createElement("a");
15554         el.hideFocus = true;
15555         el.unselectable = "on";
15556         el.href = this.href || "#";
15557         if(this.hrefTarget){
15558             el.target = this.hrefTarget;
15559         }
15560         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15561         
15562         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15563         
15564         el.innerHTML = String.format(
15565                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15566                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15567         this.el = el;
15568         Roo.menu.Item.superclass.onRender.call(this, container, position);
15569     },
15570
15571     /**
15572      * Sets the text to display in this menu item
15573      * @param {String} text The text to display
15574      * @param {Boolean} isHTML true to indicate text is pure html.
15575      */
15576     setText : function(text, isHTML){
15577         if (isHTML) {
15578             this.html = text;
15579         } else {
15580             this.text = text;
15581             this.html = '';
15582         }
15583         if(this.rendered){
15584             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15585      
15586             this.el.update(String.format(
15587                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15588                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15589             this.parentMenu.autoWidth();
15590         }
15591     },
15592
15593     // private
15594     handleClick : function(e){
15595         if(!this.href){ // if no link defined, stop the event automatically
15596             e.stopEvent();
15597         }
15598         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15599     },
15600
15601     // private
15602     activate : function(autoExpand){
15603         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15604             this.focus();
15605             if(autoExpand){
15606                 this.expandMenu();
15607             }
15608         }
15609         return true;
15610     },
15611
15612     // private
15613     shouldDeactivate : function(e){
15614         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15615             if(this.menu && this.menu.isVisible()){
15616                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15617             }
15618             return true;
15619         }
15620         return false;
15621     },
15622
15623     // private
15624     deactivate : function(){
15625         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15626         this.hideMenu();
15627     },
15628
15629     // private
15630     expandMenu : function(autoActivate){
15631         if(!this.disabled && this.menu){
15632             clearTimeout(this.hideTimer);
15633             delete this.hideTimer;
15634             if(!this.menu.isVisible() && !this.showTimer){
15635                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15636             }else if (this.menu.isVisible() && autoActivate){
15637                 this.menu.tryActivate(0, 1);
15638             }
15639         }
15640     },
15641
15642     // private
15643     deferExpand : function(autoActivate){
15644         delete this.showTimer;
15645         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15646         if(autoActivate){
15647             this.menu.tryActivate(0, 1);
15648         }
15649     },
15650
15651     // private
15652     hideMenu : function(){
15653         clearTimeout(this.showTimer);
15654         delete this.showTimer;
15655         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15656             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15657         }
15658     },
15659
15660     // private
15661     deferHide : function(){
15662         delete this.hideTimer;
15663         this.menu.hide();
15664     }
15665 });/*
15666  * Based on:
15667  * Ext JS Library 1.1.1
15668  * Copyright(c) 2006-2007, Ext JS, LLC.
15669  *
15670  * Originally Released Under LGPL - original licence link has changed is not relivant.
15671  *
15672  * Fork - LGPL
15673  * <script type="text/javascript">
15674  */
15675  
15676 /**
15677  * @class Roo.menu.CheckItem
15678  * @extends Roo.menu.Item
15679  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15680  * @constructor
15681  * Creates a new CheckItem
15682  * @param {Object} config Configuration options
15683  */
15684 Roo.menu.CheckItem = function(config){
15685     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15686     this.addEvents({
15687         /**
15688          * @event beforecheckchange
15689          * Fires before the checked value is set, providing an opportunity to cancel if needed
15690          * @param {Roo.menu.CheckItem} this
15691          * @param {Boolean} checked The new checked value that will be set
15692          */
15693         "beforecheckchange" : true,
15694         /**
15695          * @event checkchange
15696          * Fires after the checked value has been set
15697          * @param {Roo.menu.CheckItem} this
15698          * @param {Boolean} checked The checked value that was set
15699          */
15700         "checkchange" : true
15701     });
15702     if(this.checkHandler){
15703         this.on('checkchange', this.checkHandler, this.scope);
15704     }
15705 };
15706 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15707     /**
15708      * @cfg {String} group
15709      * All check items with the same group name will automatically be grouped into a single-select
15710      * radio button group (defaults to '')
15711      */
15712     /**
15713      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15714      */
15715     itemCls : "x-menu-item x-menu-check-item",
15716     /**
15717      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15718      */
15719     groupClass : "x-menu-group-item",
15720
15721     /**
15722      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15723      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15724      * initialized with checked = true will be rendered as checked.
15725      */
15726     checked: false,
15727
15728     // private
15729     ctype: "Roo.menu.CheckItem",
15730
15731     // private
15732     onRender : function(c){
15733         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15734         if(this.group){
15735             this.el.addClass(this.groupClass);
15736         }
15737         Roo.menu.MenuMgr.registerCheckable(this);
15738         if(this.checked){
15739             this.checked = false;
15740             this.setChecked(true, true);
15741         }
15742     },
15743
15744     // private
15745     destroy : function(){
15746         if(this.rendered){
15747             Roo.menu.MenuMgr.unregisterCheckable(this);
15748         }
15749         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15750     },
15751
15752     /**
15753      * Set the checked state of this item
15754      * @param {Boolean} checked The new checked value
15755      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15756      */
15757     setChecked : function(state, suppressEvent){
15758         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15759             if(this.container){
15760                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15761             }
15762             this.checked = state;
15763             if(suppressEvent !== true){
15764                 this.fireEvent("checkchange", this, state);
15765             }
15766         }
15767     },
15768
15769     // private
15770     handleClick : function(e){
15771        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15772            this.setChecked(!this.checked);
15773        }
15774        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15775     }
15776 });/*
15777  * Based on:
15778  * Ext JS Library 1.1.1
15779  * Copyright(c) 2006-2007, Ext JS, LLC.
15780  *
15781  * Originally Released Under LGPL - original licence link has changed is not relivant.
15782  *
15783  * Fork - LGPL
15784  * <script type="text/javascript">
15785  */
15786  
15787 /**
15788  * @class Roo.menu.DateItem
15789  * @extends Roo.menu.Adapter
15790  * A menu item that wraps the {@link Roo.DatPicker} component.
15791  * @constructor
15792  * Creates a new DateItem
15793  * @param {Object} config Configuration options
15794  */
15795 Roo.menu.DateItem = function(config){
15796     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15797     /** The Roo.DatePicker object @type Roo.DatePicker */
15798     this.picker = this.component;
15799     this.addEvents({select: true});
15800     
15801     this.picker.on("render", function(picker){
15802         picker.getEl().swallowEvent("click");
15803         picker.container.addClass("x-menu-date-item");
15804     });
15805
15806     this.picker.on("select", this.onSelect, this);
15807 };
15808
15809 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15810     // private
15811     onSelect : function(picker, date){
15812         this.fireEvent("select", this, date, picker);
15813         Roo.menu.DateItem.superclass.handleClick.call(this);
15814     }
15815 });/*
15816  * Based on:
15817  * Ext JS Library 1.1.1
15818  * Copyright(c) 2006-2007, Ext JS, LLC.
15819  *
15820  * Originally Released Under LGPL - original licence link has changed is not relivant.
15821  *
15822  * Fork - LGPL
15823  * <script type="text/javascript">
15824  */
15825  
15826 /**
15827  * @class Roo.menu.ColorItem
15828  * @extends Roo.menu.Adapter
15829  * A menu item that wraps the {@link Roo.ColorPalette} component.
15830  * @constructor
15831  * Creates a new ColorItem
15832  * @param {Object} config Configuration options
15833  */
15834 Roo.menu.ColorItem = function(config){
15835     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15836     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15837     this.palette = this.component;
15838     this.relayEvents(this.palette, ["select"]);
15839     if(this.selectHandler){
15840         this.on('select', this.selectHandler, this.scope);
15841     }
15842 };
15843 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15844  * Based on:
15845  * Ext JS Library 1.1.1
15846  * Copyright(c) 2006-2007, Ext JS, LLC.
15847  *
15848  * Originally Released Under LGPL - original licence link has changed is not relivant.
15849  *
15850  * Fork - LGPL
15851  * <script type="text/javascript">
15852  */
15853  
15854
15855 /**
15856  * @class Roo.menu.DateMenu
15857  * @extends Roo.menu.Menu
15858  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15859  * @constructor
15860  * Creates a new DateMenu
15861  * @param {Object} config Configuration options
15862  */
15863 Roo.menu.DateMenu = function(config){
15864     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15865     this.plain = true;
15866     var di = new Roo.menu.DateItem(config);
15867     this.add(di);
15868     /**
15869      * The {@link Roo.DatePicker} instance for this DateMenu
15870      * @type DatePicker
15871      */
15872     this.picker = di.picker;
15873     /**
15874      * @event select
15875      * @param {DatePicker} picker
15876      * @param {Date} date
15877      */
15878     this.relayEvents(di, ["select"]);
15879     this.on('beforeshow', function(){
15880         if(this.picker){
15881             this.picker.hideMonthPicker(false);
15882         }
15883     }, this);
15884 };
15885 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15886     cls:'x-date-menu'
15887 });/*
15888  * Based on:
15889  * Ext JS Library 1.1.1
15890  * Copyright(c) 2006-2007, Ext JS, LLC.
15891  *
15892  * Originally Released Under LGPL - original licence link has changed is not relivant.
15893  *
15894  * Fork - LGPL
15895  * <script type="text/javascript">
15896  */
15897  
15898
15899 /**
15900  * @class Roo.menu.ColorMenu
15901  * @extends Roo.menu.Menu
15902  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15903  * @constructor
15904  * Creates a new ColorMenu
15905  * @param {Object} config Configuration options
15906  */
15907 Roo.menu.ColorMenu = function(config){
15908     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15909     this.plain = true;
15910     var ci = new Roo.menu.ColorItem(config);
15911     this.add(ci);
15912     /**
15913      * The {@link Roo.ColorPalette} instance for this ColorMenu
15914      * @type ColorPalette
15915      */
15916     this.palette = ci.palette;
15917     /**
15918      * @event select
15919      * @param {ColorPalette} palette
15920      * @param {String} color
15921      */
15922     this.relayEvents(ci, ["select"]);
15923 };
15924 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15925  * Based on:
15926  * Ext JS Library 1.1.1
15927  * Copyright(c) 2006-2007, Ext JS, LLC.
15928  *
15929  * Originally Released Under LGPL - original licence link has changed is not relivant.
15930  *
15931  * Fork - LGPL
15932  * <script type="text/javascript">
15933  */
15934  
15935 /**
15936  * @class Roo.form.TextItem
15937  * @extends Roo.BoxComponent
15938  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15939  * @constructor
15940  * Creates a new TextItem
15941  * @param {Object} config Configuration options
15942  */
15943 Roo.form.TextItem = function(config){
15944     Roo.form.TextItem.superclass.constructor.call(this, config);
15945 };
15946
15947 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15948     
15949     /**
15950      * @cfg {String} tag the tag for this item (default div)
15951      */
15952     tag : 'div',
15953     /**
15954      * @cfg {String} html the content for this item
15955      */
15956     html : '',
15957     
15958     getAutoCreate : function()
15959     {
15960         var cfg = {
15961             id: this.id,
15962             tag: this.tag,
15963             html: this.html,
15964             cls: 'x-form-item'
15965         };
15966         
15967         return cfg;
15968         
15969     },
15970     
15971     onRender : function(ct, position)
15972     {
15973         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15974         
15975         if(!this.el){
15976             var cfg = this.getAutoCreate();
15977             if(!cfg.name){
15978                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15979             }
15980             if (!cfg.name.length) {
15981                 delete cfg.name;
15982             }
15983             this.el = ct.createChild(cfg, position);
15984         }
15985     },
15986     /*
15987      * setHTML
15988      * @param {String} html update the Contents of the element.
15989      */
15990     setHTML : function(html)
15991     {
15992         this.fieldEl.dom.innerHTML = html;
15993     }
15994     
15995 });/*
15996  * Based on:
15997  * Ext JS Library 1.1.1
15998  * Copyright(c) 2006-2007, Ext JS, LLC.
15999  *
16000  * Originally Released Under LGPL - original licence link has changed is not relivant.
16001  *
16002  * Fork - LGPL
16003  * <script type="text/javascript">
16004  */
16005  
16006 /**
16007  * @class Roo.form.Field
16008  * @extends Roo.BoxComponent
16009  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16010  * @constructor
16011  * Creates a new Field
16012  * @param {Object} config Configuration options
16013  */
16014 Roo.form.Field = function(config){
16015     Roo.form.Field.superclass.constructor.call(this, config);
16016 };
16017
16018 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16019     /**
16020      * @cfg {String} fieldLabel Label to use when rendering a form.
16021      */
16022        /**
16023      * @cfg {String} qtip Mouse over tip
16024      */
16025      
16026     /**
16027      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16028      */
16029     invalidClass : "x-form-invalid",
16030     /**
16031      * @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")
16032      */
16033     invalidText : "The value in this field is invalid",
16034     /**
16035      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16036      */
16037     focusClass : "x-form-focus",
16038     /**
16039      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16040       automatic validation (defaults to "keyup").
16041      */
16042     validationEvent : "keyup",
16043     /**
16044      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16045      */
16046     validateOnBlur : true,
16047     /**
16048      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16049      */
16050     validationDelay : 250,
16051     /**
16052      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16053      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16054      */
16055     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16056     /**
16057      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16058      */
16059     fieldClass : "x-form-field",
16060     /**
16061      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16062      *<pre>
16063 Value         Description
16064 -----------   ----------------------------------------------------------------------
16065 qtip          Display a quick tip when the user hovers over the field
16066 title         Display a default browser title attribute popup
16067 under         Add a block div beneath the field containing the error text
16068 side          Add an error icon to the right of the field with a popup on hover
16069 [element id]  Add the error text directly to the innerHTML of the specified element
16070 </pre>
16071      */
16072     msgTarget : 'qtip',
16073     /**
16074      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16075      */
16076     msgFx : 'normal',
16077
16078     /**
16079      * @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.
16080      */
16081     readOnly : false,
16082
16083     /**
16084      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16085      */
16086     disabled : false,
16087
16088     /**
16089      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16090      */
16091     inputType : undefined,
16092     
16093     /**
16094      * @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).
16095          */
16096         tabIndex : undefined,
16097         
16098     // private
16099     isFormField : true,
16100
16101     // private
16102     hasFocus : false,
16103     /**
16104      * @property {Roo.Element} fieldEl
16105      * Element Containing the rendered Field (with label etc.)
16106      */
16107     /**
16108      * @cfg {Mixed} value A value to initialize this field with.
16109      */
16110     value : undefined,
16111
16112     /**
16113      * @cfg {String} name The field's HTML name attribute.
16114      */
16115     /**
16116      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16117      */
16118     // private
16119     loadedValue : false,
16120      
16121      
16122         // private ??
16123         initComponent : function(){
16124         Roo.form.Field.superclass.initComponent.call(this);
16125         this.addEvents({
16126             /**
16127              * @event focus
16128              * Fires when this field receives input focus.
16129              * @param {Roo.form.Field} this
16130              */
16131             focus : true,
16132             /**
16133              * @event blur
16134              * Fires when this field loses input focus.
16135              * @param {Roo.form.Field} this
16136              */
16137             blur : true,
16138             /**
16139              * @event specialkey
16140              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16141              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16142              * @param {Roo.form.Field} this
16143              * @param {Roo.EventObject} e The event object
16144              */
16145             specialkey : true,
16146             /**
16147              * @event change
16148              * Fires just before the field blurs if the field value has changed.
16149              * @param {Roo.form.Field} this
16150              * @param {Mixed} newValue The new value
16151              * @param {Mixed} oldValue The original value
16152              */
16153             change : true,
16154             /**
16155              * @event invalid
16156              * Fires after the field has been marked as invalid.
16157              * @param {Roo.form.Field} this
16158              * @param {String} msg The validation message
16159              */
16160             invalid : true,
16161             /**
16162              * @event valid
16163              * Fires after the field has been validated with no errors.
16164              * @param {Roo.form.Field} this
16165              */
16166             valid : true,
16167              /**
16168              * @event keyup
16169              * Fires after the key up
16170              * @param {Roo.form.Field} this
16171              * @param {Roo.EventObject}  e The event Object
16172              */
16173             keyup : true
16174         });
16175     },
16176
16177     /**
16178      * Returns the name attribute of the field if available
16179      * @return {String} name The field name
16180      */
16181     getName: function(){
16182          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16183     },
16184
16185     // private
16186     onRender : function(ct, position){
16187         Roo.form.Field.superclass.onRender.call(this, ct, position);
16188         if(!this.el){
16189             var cfg = this.getAutoCreate();
16190             if(!cfg.name){
16191                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16192             }
16193             if (!cfg.name.length) {
16194                 delete cfg.name;
16195             }
16196             if(this.inputType){
16197                 cfg.type = this.inputType;
16198             }
16199             this.el = ct.createChild(cfg, position);
16200         }
16201         var type = this.el.dom.type;
16202         if(type){
16203             if(type == 'password'){
16204                 type = 'text';
16205             }
16206             this.el.addClass('x-form-'+type);
16207         }
16208         if(this.readOnly){
16209             this.el.dom.readOnly = true;
16210         }
16211         if(this.tabIndex !== undefined){
16212             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16213         }
16214
16215         this.el.addClass([this.fieldClass, this.cls]);
16216         this.initValue();
16217     },
16218
16219     /**
16220      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16221      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16222      * @return {Roo.form.Field} this
16223      */
16224     applyTo : function(target){
16225         this.allowDomMove = false;
16226         this.el = Roo.get(target);
16227         this.render(this.el.dom.parentNode);
16228         return this;
16229     },
16230
16231     // private
16232     initValue : function(){
16233         if(this.value !== undefined){
16234             this.setValue(this.value);
16235         }else if(this.el.dom.value.length > 0){
16236             this.setValue(this.el.dom.value);
16237         }
16238     },
16239
16240     /**
16241      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16242      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16243      */
16244     isDirty : function() {
16245         if(this.disabled) {
16246             return false;
16247         }
16248         return String(this.getValue()) !== String(this.originalValue);
16249     },
16250
16251     /**
16252      * stores the current value in loadedValue
16253      */
16254     resetHasChanged : function()
16255     {
16256         this.loadedValue = String(this.getValue());
16257     },
16258     /**
16259      * checks the current value against the 'loaded' value.
16260      * Note - will return false if 'resetHasChanged' has not been called first.
16261      */
16262     hasChanged : function()
16263     {
16264         if(this.disabled || this.readOnly) {
16265             return false;
16266         }
16267         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16268     },
16269     
16270     
16271     
16272     // private
16273     afterRender : function(){
16274         Roo.form.Field.superclass.afterRender.call(this);
16275         this.initEvents();
16276     },
16277
16278     // private
16279     fireKey : function(e){
16280         //Roo.log('field ' + e.getKey());
16281         if(e.isNavKeyPress()){
16282             this.fireEvent("specialkey", this, e);
16283         }
16284     },
16285
16286     /**
16287      * Resets the current field value to the originally loaded value and clears any validation messages
16288      */
16289     reset : function(){
16290         this.setValue(this.resetValue);
16291         this.originalValue = this.getValue();
16292         this.clearInvalid();
16293     },
16294
16295     // private
16296     initEvents : function(){
16297         // safari killled keypress - so keydown is now used..
16298         this.el.on("keydown" , this.fireKey,  this);
16299         this.el.on("focus", this.onFocus,  this);
16300         this.el.on("blur", this.onBlur,  this);
16301         this.el.relayEvent('keyup', this);
16302
16303         // reference to original value for reset
16304         this.originalValue = this.getValue();
16305         this.resetValue =  this.getValue();
16306     },
16307
16308     // private
16309     onFocus : function(){
16310         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16311             this.el.addClass(this.focusClass);
16312         }
16313         if(!this.hasFocus){
16314             this.hasFocus = true;
16315             this.startValue = this.getValue();
16316             this.fireEvent("focus", this);
16317         }
16318     },
16319
16320     beforeBlur : Roo.emptyFn,
16321
16322     // private
16323     onBlur : function(){
16324         this.beforeBlur();
16325         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16326             this.el.removeClass(this.focusClass);
16327         }
16328         this.hasFocus = false;
16329         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16330             this.validate();
16331         }
16332         var v = this.getValue();
16333         if(String(v) !== String(this.startValue)){
16334             this.fireEvent('change', this, v, this.startValue);
16335         }
16336         this.fireEvent("blur", this);
16337     },
16338
16339     /**
16340      * Returns whether or not the field value is currently valid
16341      * @param {Boolean} preventMark True to disable marking the field invalid
16342      * @return {Boolean} True if the value is valid, else false
16343      */
16344     isValid : function(preventMark){
16345         if(this.disabled){
16346             return true;
16347         }
16348         var restore = this.preventMark;
16349         this.preventMark = preventMark === true;
16350         var v = this.validateValue(this.processValue(this.getRawValue()));
16351         this.preventMark = restore;
16352         return v;
16353     },
16354
16355     /**
16356      * Validates the field value
16357      * @return {Boolean} True if the value is valid, else false
16358      */
16359     validate : function(){
16360         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16361             this.clearInvalid();
16362             return true;
16363         }
16364         return false;
16365     },
16366
16367     processValue : function(value){
16368         return value;
16369     },
16370
16371     // private
16372     // Subclasses should provide the validation implementation by overriding this
16373     validateValue : function(value){
16374         return true;
16375     },
16376
16377     /**
16378      * Mark this field as invalid
16379      * @param {String} msg The validation message
16380      */
16381     markInvalid : function(msg){
16382         if(!this.rendered || this.preventMark){ // not rendered
16383             return;
16384         }
16385         
16386         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16387         
16388         obj.el.addClass(this.invalidClass);
16389         msg = msg || this.invalidText;
16390         switch(this.msgTarget){
16391             case 'qtip':
16392                 obj.el.dom.qtip = msg;
16393                 obj.el.dom.qclass = 'x-form-invalid-tip';
16394                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16395                     Roo.QuickTips.enable();
16396                 }
16397                 break;
16398             case 'title':
16399                 this.el.dom.title = msg;
16400                 break;
16401             case 'under':
16402                 if(!this.errorEl){
16403                     var elp = this.el.findParent('.x-form-element', 5, true);
16404                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16405                     this.errorEl.setWidth(elp.getWidth(true)-20);
16406                 }
16407                 this.errorEl.update(msg);
16408                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16409                 break;
16410             case 'side':
16411                 if(!this.errorIcon){
16412                     var elp = this.el.findParent('.x-form-element', 5, true);
16413                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16414                 }
16415                 this.alignErrorIcon();
16416                 this.errorIcon.dom.qtip = msg;
16417                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16418                 this.errorIcon.show();
16419                 this.on('resize', this.alignErrorIcon, this);
16420                 break;
16421             default:
16422                 var t = Roo.getDom(this.msgTarget);
16423                 t.innerHTML = msg;
16424                 t.style.display = this.msgDisplay;
16425                 break;
16426         }
16427         this.fireEvent('invalid', this, msg);
16428     },
16429
16430     // private
16431     alignErrorIcon : function(){
16432         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16433     },
16434
16435     /**
16436      * Clear any invalid styles/messages for this field
16437      */
16438     clearInvalid : function(){
16439         if(!this.rendered || this.preventMark){ // not rendered
16440             return;
16441         }
16442         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16443         
16444         obj.el.removeClass(this.invalidClass);
16445         switch(this.msgTarget){
16446             case 'qtip':
16447                 obj.el.dom.qtip = '';
16448                 break;
16449             case 'title':
16450                 this.el.dom.title = '';
16451                 break;
16452             case 'under':
16453                 if(this.errorEl){
16454                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16455                 }
16456                 break;
16457             case 'side':
16458                 if(this.errorIcon){
16459                     this.errorIcon.dom.qtip = '';
16460                     this.errorIcon.hide();
16461                     this.un('resize', this.alignErrorIcon, this);
16462                 }
16463                 break;
16464             default:
16465                 var t = Roo.getDom(this.msgTarget);
16466                 t.innerHTML = '';
16467                 t.style.display = 'none';
16468                 break;
16469         }
16470         this.fireEvent('valid', this);
16471     },
16472
16473     /**
16474      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16475      * @return {Mixed} value The field value
16476      */
16477     getRawValue : function(){
16478         var v = this.el.getValue();
16479         
16480         return v;
16481     },
16482
16483     /**
16484      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16485      * @return {Mixed} value The field value
16486      */
16487     getValue : function(){
16488         var v = this.el.getValue();
16489          
16490         return v;
16491     },
16492
16493     /**
16494      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16495      * @param {Mixed} value The value to set
16496      */
16497     setRawValue : function(v){
16498         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16499     },
16500
16501     /**
16502      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16503      * @param {Mixed} value The value to set
16504      */
16505     setValue : function(v){
16506         this.value = v;
16507         if(this.rendered){
16508             this.el.dom.value = (v === null || v === undefined ? '' : v);
16509              this.validate();
16510         }
16511     },
16512
16513     adjustSize : function(w, h){
16514         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16515         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16516         return s;
16517     },
16518
16519     adjustWidth : function(tag, w){
16520         tag = tag.toLowerCase();
16521         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16522             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16523                 if(tag == 'input'){
16524                     return w + 2;
16525                 }
16526                 if(tag == 'textarea'){
16527                     return w-2;
16528                 }
16529             }else if(Roo.isOpera){
16530                 if(tag == 'input'){
16531                     return w + 2;
16532                 }
16533                 if(tag == 'textarea'){
16534                     return w-2;
16535                 }
16536             }
16537         }
16538         return w;
16539     }
16540 });
16541
16542
16543 // anything other than normal should be considered experimental
16544 Roo.form.Field.msgFx = {
16545     normal : {
16546         show: function(msgEl, f){
16547             msgEl.setDisplayed('block');
16548         },
16549
16550         hide : function(msgEl, f){
16551             msgEl.setDisplayed(false).update('');
16552         }
16553     },
16554
16555     slide : {
16556         show: function(msgEl, f){
16557             msgEl.slideIn('t', {stopFx:true});
16558         },
16559
16560         hide : function(msgEl, f){
16561             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16562         }
16563     },
16564
16565     slideRight : {
16566         show: function(msgEl, f){
16567             msgEl.fixDisplay();
16568             msgEl.alignTo(f.el, 'tl-tr');
16569             msgEl.slideIn('l', {stopFx:true});
16570         },
16571
16572         hide : function(msgEl, f){
16573             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16574         }
16575     }
16576 };/*
16577  * Based on:
16578  * Ext JS Library 1.1.1
16579  * Copyright(c) 2006-2007, Ext JS, LLC.
16580  *
16581  * Originally Released Under LGPL - original licence link has changed is not relivant.
16582  *
16583  * Fork - LGPL
16584  * <script type="text/javascript">
16585  */
16586  
16587
16588 /**
16589  * @class Roo.form.TextField
16590  * @extends Roo.form.Field
16591  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16592  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16593  * @constructor
16594  * Creates a new TextField
16595  * @param {Object} config Configuration options
16596  */
16597 Roo.form.TextField = function(config){
16598     Roo.form.TextField.superclass.constructor.call(this, config);
16599     this.addEvents({
16600         /**
16601          * @event autosize
16602          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16603          * according to the default logic, but this event provides a hook for the developer to apply additional
16604          * logic at runtime to resize the field if needed.
16605              * @param {Roo.form.Field} this This text field
16606              * @param {Number} width The new field width
16607              */
16608         autosize : true
16609     });
16610 };
16611
16612 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16613     /**
16614      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16615      */
16616     grow : false,
16617     /**
16618      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16619      */
16620     growMin : 30,
16621     /**
16622      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16623      */
16624     growMax : 800,
16625     /**
16626      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16627      */
16628     vtype : null,
16629     /**
16630      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16631      */
16632     maskRe : null,
16633     /**
16634      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16635      */
16636     disableKeyFilter : false,
16637     /**
16638      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16639      */
16640     allowBlank : true,
16641     /**
16642      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16643      */
16644     minLength : 0,
16645     /**
16646      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16647      */
16648     maxLength : Number.MAX_VALUE,
16649     /**
16650      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16651      */
16652     minLengthText : "The minimum length for this field is {0}",
16653     /**
16654      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16655      */
16656     maxLengthText : "The maximum length for this field is {0}",
16657     /**
16658      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16659      */
16660     selectOnFocus : false,
16661     /**
16662      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16663      */    
16664     allowLeadingSpace : false,
16665     /**
16666      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16667      */
16668     blankText : "This field is required",
16669     /**
16670      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16671      * If available, this function will be called only after the basic validators all return true, and will be passed the
16672      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16673      */
16674     validator : null,
16675     /**
16676      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16677      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16678      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16679      */
16680     regex : null,
16681     /**
16682      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16683      */
16684     regexText : "",
16685     /**
16686      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16687      */
16688     emptyText : null,
16689    
16690
16691     // private
16692     initEvents : function()
16693     {
16694         if (this.emptyText) {
16695             this.el.attr('placeholder', this.emptyText);
16696         }
16697         
16698         Roo.form.TextField.superclass.initEvents.call(this);
16699         if(this.validationEvent == 'keyup'){
16700             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16701             this.el.on('keyup', this.filterValidation, this);
16702         }
16703         else if(this.validationEvent !== false){
16704             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16705         }
16706         
16707         if(this.selectOnFocus){
16708             this.on("focus", this.preFocus, this);
16709         }
16710         if (!this.allowLeadingSpace) {
16711             this.on('blur', this.cleanLeadingSpace, this);
16712         }
16713         
16714         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16715             this.el.on("keypress", this.filterKeys, this);
16716         }
16717         if(this.grow){
16718             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16719             this.el.on("click", this.autoSize,  this);
16720         }
16721         if(this.el.is('input[type=password]') && Roo.isSafari){
16722             this.el.on('keydown', this.SafariOnKeyDown, this);
16723         }
16724     },
16725
16726     processValue : function(value){
16727         if(this.stripCharsRe){
16728             var newValue = value.replace(this.stripCharsRe, '');
16729             if(newValue !== value){
16730                 this.setRawValue(newValue);
16731                 return newValue;
16732             }
16733         }
16734         return value;
16735     },
16736
16737     filterValidation : function(e){
16738         if(!e.isNavKeyPress()){
16739             this.validationTask.delay(this.validationDelay);
16740         }
16741     },
16742
16743     // private
16744     onKeyUp : function(e){
16745         if(!e.isNavKeyPress()){
16746             this.autoSize();
16747         }
16748     },
16749     // private - clean the leading white space
16750     cleanLeadingSpace : function(e)
16751     {
16752         if ( this.inputType == 'file') {
16753             return;
16754         }
16755         
16756         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16757     },
16758     /**
16759      * Resets the current field value to the originally-loaded value and clears any validation messages.
16760      *  
16761      */
16762     reset : function(){
16763         Roo.form.TextField.superclass.reset.call(this);
16764        
16765     }, 
16766     // private
16767     preFocus : function(){
16768         
16769         if(this.selectOnFocus){
16770             this.el.dom.select();
16771         }
16772     },
16773
16774     
16775     // private
16776     filterKeys : function(e){
16777         var k = e.getKey();
16778         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16779             return;
16780         }
16781         var c = e.getCharCode(), cc = String.fromCharCode(c);
16782         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16783             return;
16784         }
16785         if(!this.maskRe.test(cc)){
16786             e.stopEvent();
16787         }
16788     },
16789
16790     setValue : function(v){
16791         
16792         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16793         
16794         this.autoSize();
16795     },
16796
16797     /**
16798      * Validates a value according to the field's validation rules and marks the field as invalid
16799      * if the validation fails
16800      * @param {Mixed} value The value to validate
16801      * @return {Boolean} True if the value is valid, else false
16802      */
16803     validateValue : function(value){
16804         if(value.length < 1)  { // if it's blank
16805              if(this.allowBlank){
16806                 this.clearInvalid();
16807                 return true;
16808              }else{
16809                 this.markInvalid(this.blankText);
16810                 return false;
16811              }
16812         }
16813         if(value.length < this.minLength){
16814             this.markInvalid(String.format(this.minLengthText, this.minLength));
16815             return false;
16816         }
16817         if(value.length > this.maxLength){
16818             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16819             return false;
16820         }
16821         if(this.vtype){
16822             var vt = Roo.form.VTypes;
16823             if(!vt[this.vtype](value, this)){
16824                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16825                 return false;
16826             }
16827         }
16828         if(typeof this.validator == "function"){
16829             var msg = this.validator(value);
16830             if(msg !== true){
16831                 this.markInvalid(msg);
16832                 return false;
16833             }
16834         }
16835         if(this.regex && !this.regex.test(value)){
16836             this.markInvalid(this.regexText);
16837             return false;
16838         }
16839         return true;
16840     },
16841
16842     /**
16843      * Selects text in this field
16844      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16845      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16846      */
16847     selectText : function(start, end){
16848         var v = this.getRawValue();
16849         if(v.length > 0){
16850             start = start === undefined ? 0 : start;
16851             end = end === undefined ? v.length : end;
16852             var d = this.el.dom;
16853             if(d.setSelectionRange){
16854                 d.setSelectionRange(start, end);
16855             }else if(d.createTextRange){
16856                 var range = d.createTextRange();
16857                 range.moveStart("character", start);
16858                 range.moveEnd("character", v.length-end);
16859                 range.select();
16860             }
16861         }
16862     },
16863
16864     /**
16865      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16866      * This only takes effect if grow = true, and fires the autosize event.
16867      */
16868     autoSize : function(){
16869         if(!this.grow || !this.rendered){
16870             return;
16871         }
16872         if(!this.metrics){
16873             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16874         }
16875         var el = this.el;
16876         var v = el.dom.value;
16877         var d = document.createElement('div');
16878         d.appendChild(document.createTextNode(v));
16879         v = d.innerHTML;
16880         d = null;
16881         v += "&#160;";
16882         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16883         this.el.setWidth(w);
16884         this.fireEvent("autosize", this, w);
16885     },
16886     
16887     // private
16888     SafariOnKeyDown : function(event)
16889     {
16890         // this is a workaround for a password hang bug on chrome/ webkit.
16891         
16892         var isSelectAll = false;
16893         
16894         if(this.el.dom.selectionEnd > 0){
16895             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16896         }
16897         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16898             event.preventDefault();
16899             this.setValue('');
16900             return;
16901         }
16902         
16903         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16904             
16905             event.preventDefault();
16906             // this is very hacky as keydown always get's upper case.
16907             
16908             var cc = String.fromCharCode(event.getCharCode());
16909             
16910             
16911             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16912             
16913         }
16914         
16915         
16916     }
16917 });/*
16918  * Based on:
16919  * Ext JS Library 1.1.1
16920  * Copyright(c) 2006-2007, Ext JS, LLC.
16921  *
16922  * Originally Released Under LGPL - original licence link has changed is not relivant.
16923  *
16924  * Fork - LGPL
16925  * <script type="text/javascript">
16926  */
16927  
16928 /**
16929  * @class Roo.form.Hidden
16930  * @extends Roo.form.TextField
16931  * Simple Hidden element used on forms 
16932  * 
16933  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16934  * 
16935  * @constructor
16936  * Creates a new Hidden form element.
16937  * @param {Object} config Configuration options
16938  */
16939
16940
16941
16942 // easy hidden field...
16943 Roo.form.Hidden = function(config){
16944     Roo.form.Hidden.superclass.constructor.call(this, config);
16945 };
16946   
16947 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16948     fieldLabel:      '',
16949     inputType:      'hidden',
16950     width:          50,
16951     allowBlank:     true,
16952     labelSeparator: '',
16953     hidden:         true,
16954     itemCls :       'x-form-item-display-none'
16955
16956
16957 });
16958
16959
16960 /*
16961  * Based on:
16962  * Ext JS Library 1.1.1
16963  * Copyright(c) 2006-2007, Ext JS, LLC.
16964  *
16965  * Originally Released Under LGPL - original licence link has changed is not relivant.
16966  *
16967  * Fork - LGPL
16968  * <script type="text/javascript">
16969  */
16970  
16971 /**
16972  * @class Roo.form.TriggerField
16973  * @extends Roo.form.TextField
16974  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16975  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16976  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16977  * for which you can provide a custom implementation.  For example:
16978  * <pre><code>
16979 var trigger = new Roo.form.TriggerField();
16980 trigger.onTriggerClick = myTriggerFn;
16981 trigger.applyTo('my-field');
16982 </code></pre>
16983  *
16984  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16985  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16986  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16987  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16988  * @constructor
16989  * Create a new TriggerField.
16990  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16991  * to the base TextField)
16992  */
16993 Roo.form.TriggerField = function(config){
16994     this.mimicing = false;
16995     Roo.form.TriggerField.superclass.constructor.call(this, config);
16996 };
16997
16998 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16999     /**
17000      * @cfg {String} triggerClass A CSS class to apply to the trigger
17001      */
17002     /**
17003      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17004      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17005      */
17006     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17007     /**
17008      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17009      */
17010     hideTrigger:false,
17011
17012     /** @cfg {Boolean} grow @hide */
17013     /** @cfg {Number} growMin @hide */
17014     /** @cfg {Number} growMax @hide */
17015
17016     /**
17017      * @hide 
17018      * @method
17019      */
17020     autoSize: Roo.emptyFn,
17021     // private
17022     monitorTab : true,
17023     // private
17024     deferHeight : true,
17025
17026     
17027     actionMode : 'wrap',
17028     // private
17029     onResize : function(w, h){
17030         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17031         if(typeof w == 'number'){
17032             var x = w - this.trigger.getWidth();
17033             this.el.setWidth(this.adjustWidth('input', x));
17034             this.trigger.setStyle('left', x+'px');
17035         }
17036     },
17037
17038     // private
17039     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17040
17041     // private
17042     getResizeEl : function(){
17043         return this.wrap;
17044     },
17045
17046     // private
17047     getPositionEl : function(){
17048         return this.wrap;
17049     },
17050
17051     // private
17052     alignErrorIcon : function(){
17053         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17054     },
17055
17056     // private
17057     onRender : function(ct, position){
17058         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17059         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17060         this.trigger = this.wrap.createChild(this.triggerConfig ||
17061                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17062         if(this.hideTrigger){
17063             this.trigger.setDisplayed(false);
17064         }
17065         this.initTrigger();
17066         if(!this.width){
17067             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17068         }
17069     },
17070
17071     // private
17072     initTrigger : function(){
17073         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17074         this.trigger.addClassOnOver('x-form-trigger-over');
17075         this.trigger.addClassOnClick('x-form-trigger-click');
17076     },
17077
17078     // private
17079     onDestroy : function(){
17080         if(this.trigger){
17081             this.trigger.removeAllListeners();
17082             this.trigger.remove();
17083         }
17084         if(this.wrap){
17085             this.wrap.remove();
17086         }
17087         Roo.form.TriggerField.superclass.onDestroy.call(this);
17088     },
17089
17090     // private
17091     onFocus : function(){
17092         Roo.form.TriggerField.superclass.onFocus.call(this);
17093         if(!this.mimicing){
17094             this.wrap.addClass('x-trigger-wrap-focus');
17095             this.mimicing = true;
17096             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17097             if(this.monitorTab){
17098                 this.el.on("keydown", this.checkTab, this);
17099             }
17100         }
17101     },
17102
17103     // private
17104     checkTab : function(e){
17105         if(e.getKey() == e.TAB){
17106             this.triggerBlur();
17107         }
17108     },
17109
17110     // private
17111     onBlur : function(){
17112         // do nothing
17113     },
17114
17115     // private
17116     mimicBlur : function(e, t){
17117         if(!this.wrap.contains(t) && this.validateBlur()){
17118             this.triggerBlur();
17119         }
17120     },
17121
17122     // private
17123     triggerBlur : function(){
17124         this.mimicing = false;
17125         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17126         if(this.monitorTab){
17127             this.el.un("keydown", this.checkTab, this);
17128         }
17129         this.wrap.removeClass('x-trigger-wrap-focus');
17130         Roo.form.TriggerField.superclass.onBlur.call(this);
17131     },
17132
17133     // private
17134     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17135     validateBlur : function(e, t){
17136         return true;
17137     },
17138
17139     // private
17140     onDisable : function(){
17141         Roo.form.TriggerField.superclass.onDisable.call(this);
17142         if(this.wrap){
17143             this.wrap.addClass('x-item-disabled');
17144         }
17145     },
17146
17147     // private
17148     onEnable : function(){
17149         Roo.form.TriggerField.superclass.onEnable.call(this);
17150         if(this.wrap){
17151             this.wrap.removeClass('x-item-disabled');
17152         }
17153     },
17154
17155     // private
17156     onShow : function(){
17157         var ae = this.getActionEl();
17158         
17159         if(ae){
17160             ae.dom.style.display = '';
17161             ae.dom.style.visibility = 'visible';
17162         }
17163     },
17164
17165     // private
17166     
17167     onHide : function(){
17168         var ae = this.getActionEl();
17169         ae.dom.style.display = 'none';
17170     },
17171
17172     /**
17173      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17174      * by an implementing function.
17175      * @method
17176      * @param {EventObject} e
17177      */
17178     onTriggerClick : Roo.emptyFn
17179 });
17180
17181 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17182 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17183 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17184 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17185     initComponent : function(){
17186         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17187
17188         this.triggerConfig = {
17189             tag:'span', cls:'x-form-twin-triggers', cn:[
17190             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17191             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17192         ]};
17193     },
17194
17195     getTrigger : function(index){
17196         return this.triggers[index];
17197     },
17198
17199     initTrigger : function(){
17200         var ts = this.trigger.select('.x-form-trigger', true);
17201         this.wrap.setStyle('overflow', 'hidden');
17202         var triggerField = this;
17203         ts.each(function(t, all, index){
17204             t.hide = function(){
17205                 var w = triggerField.wrap.getWidth();
17206                 this.dom.style.display = 'none';
17207                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17208             };
17209             t.show = function(){
17210                 var w = triggerField.wrap.getWidth();
17211                 this.dom.style.display = '';
17212                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17213             };
17214             var triggerIndex = 'Trigger'+(index+1);
17215
17216             if(this['hide'+triggerIndex]){
17217                 t.dom.style.display = 'none';
17218             }
17219             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17220             t.addClassOnOver('x-form-trigger-over');
17221             t.addClassOnClick('x-form-trigger-click');
17222         }, this);
17223         this.triggers = ts.elements;
17224     },
17225
17226     onTrigger1Click : Roo.emptyFn,
17227     onTrigger2Click : Roo.emptyFn
17228 });/*
17229  * Based on:
17230  * Ext JS Library 1.1.1
17231  * Copyright(c) 2006-2007, Ext JS, LLC.
17232  *
17233  * Originally Released Under LGPL - original licence link has changed is not relivant.
17234  *
17235  * Fork - LGPL
17236  * <script type="text/javascript">
17237  */
17238  
17239 /**
17240  * @class Roo.form.TextArea
17241  * @extends Roo.form.TextField
17242  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17243  * support for auto-sizing.
17244  * @constructor
17245  * Creates a new TextArea
17246  * @param {Object} config Configuration options
17247  */
17248 Roo.form.TextArea = function(config){
17249     Roo.form.TextArea.superclass.constructor.call(this, config);
17250     // these are provided exchanges for backwards compat
17251     // minHeight/maxHeight were replaced by growMin/growMax to be
17252     // compatible with TextField growing config values
17253     if(this.minHeight !== undefined){
17254         this.growMin = this.minHeight;
17255     }
17256     if(this.maxHeight !== undefined){
17257         this.growMax = this.maxHeight;
17258     }
17259 };
17260
17261 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17262     /**
17263      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17264      */
17265     growMin : 60,
17266     /**
17267      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17268      */
17269     growMax: 1000,
17270     /**
17271      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17272      * in the field (equivalent to setting overflow: hidden, defaults to false)
17273      */
17274     preventScrollbars: false,
17275     /**
17276      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17277      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17278      */
17279
17280     // private
17281     onRender : function(ct, position){
17282         if(!this.el){
17283             this.defaultAutoCreate = {
17284                 tag: "textarea",
17285                 style:"width:300px;height:60px;",
17286                 autocomplete: "new-password"
17287             };
17288         }
17289         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17290         if(this.grow){
17291             this.textSizeEl = Roo.DomHelper.append(document.body, {
17292                 tag: "pre", cls: "x-form-grow-sizer"
17293             });
17294             if(this.preventScrollbars){
17295                 this.el.setStyle("overflow", "hidden");
17296             }
17297             this.el.setHeight(this.growMin);
17298         }
17299     },
17300
17301     onDestroy : function(){
17302         if(this.textSizeEl){
17303             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17304         }
17305         Roo.form.TextArea.superclass.onDestroy.call(this);
17306     },
17307
17308     // private
17309     onKeyUp : function(e){
17310         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17311             this.autoSize();
17312         }
17313     },
17314
17315     /**
17316      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17317      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17318      */
17319     autoSize : function(){
17320         if(!this.grow || !this.textSizeEl){
17321             return;
17322         }
17323         var el = this.el;
17324         var v = el.dom.value;
17325         var ts = this.textSizeEl;
17326
17327         ts.innerHTML = '';
17328         ts.appendChild(document.createTextNode(v));
17329         v = ts.innerHTML;
17330
17331         Roo.fly(ts).setWidth(this.el.getWidth());
17332         if(v.length < 1){
17333             v = "&#160;&#160;";
17334         }else{
17335             if(Roo.isIE){
17336                 v = v.replace(/\n/g, '<p>&#160;</p>');
17337             }
17338             v += "&#160;\n&#160;";
17339         }
17340         ts.innerHTML = v;
17341         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17342         if(h != this.lastHeight){
17343             this.lastHeight = h;
17344             this.el.setHeight(h);
17345             this.fireEvent("autosize", this, h);
17346         }
17347     }
17348 });/*
17349  * Based on:
17350  * Ext JS Library 1.1.1
17351  * Copyright(c) 2006-2007, Ext JS, LLC.
17352  *
17353  * Originally Released Under LGPL - original licence link has changed is not relivant.
17354  *
17355  * Fork - LGPL
17356  * <script type="text/javascript">
17357  */
17358  
17359
17360 /**
17361  * @class Roo.form.NumberField
17362  * @extends Roo.form.TextField
17363  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17364  * @constructor
17365  * Creates a new NumberField
17366  * @param {Object} config Configuration options
17367  */
17368 Roo.form.NumberField = function(config){
17369     Roo.form.NumberField.superclass.constructor.call(this, config);
17370 };
17371
17372 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17373     /**
17374      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17375      */
17376     fieldClass: "x-form-field x-form-num-field",
17377     /**
17378      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17379      */
17380     allowDecimals : true,
17381     /**
17382      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17383      */
17384     decimalSeparator : ".",
17385     /**
17386      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17387      */
17388     decimalPrecision : 2,
17389     /**
17390      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17391      */
17392     allowNegative : true,
17393     /**
17394      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17395      */
17396     minValue : Number.NEGATIVE_INFINITY,
17397     /**
17398      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17399      */
17400     maxValue : Number.MAX_VALUE,
17401     /**
17402      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17403      */
17404     minText : "The minimum value for this field is {0}",
17405     /**
17406      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17407      */
17408     maxText : "The maximum value for this field is {0}",
17409     /**
17410      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17411      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17412      */
17413     nanText : "{0} is not a valid number",
17414
17415     // private
17416     initEvents : function(){
17417         Roo.form.NumberField.superclass.initEvents.call(this);
17418         var allowed = "0123456789";
17419         if(this.allowDecimals){
17420             allowed += this.decimalSeparator;
17421         }
17422         if(this.allowNegative){
17423             allowed += "-";
17424         }
17425         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17426         var keyPress = function(e){
17427             var k = e.getKey();
17428             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17429                 return;
17430             }
17431             var c = e.getCharCode();
17432             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17433                 e.stopEvent();
17434             }
17435         };
17436         this.el.on("keypress", keyPress, this);
17437     },
17438
17439     // private
17440     validateValue : function(value){
17441         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17442             return false;
17443         }
17444         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17445              return true;
17446         }
17447         var num = this.parseValue(value);
17448         if(isNaN(num)){
17449             this.markInvalid(String.format(this.nanText, value));
17450             return false;
17451         }
17452         if(num < this.minValue){
17453             this.markInvalid(String.format(this.minText, this.minValue));
17454             return false;
17455         }
17456         if(num > this.maxValue){
17457             this.markInvalid(String.format(this.maxText, this.maxValue));
17458             return false;
17459         }
17460         return true;
17461     },
17462
17463     getValue : function(){
17464         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17465     },
17466
17467     // private
17468     parseValue : function(value){
17469         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17470         return isNaN(value) ? '' : value;
17471     },
17472
17473     // private
17474     fixPrecision : function(value){
17475         var nan = isNaN(value);
17476         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17477             return nan ? '' : value;
17478         }
17479         return parseFloat(value).toFixed(this.decimalPrecision);
17480     },
17481
17482     setValue : function(v){
17483         v = this.fixPrecision(v);
17484         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17485     },
17486
17487     // private
17488     decimalPrecisionFcn : function(v){
17489         return Math.floor(v);
17490     },
17491
17492     beforeBlur : function(){
17493         var v = this.parseValue(this.getRawValue());
17494         if(v){
17495             this.setValue(v);
17496         }
17497     }
17498 });/*
17499  * Based on:
17500  * Ext JS Library 1.1.1
17501  * Copyright(c) 2006-2007, Ext JS, LLC.
17502  *
17503  * Originally Released Under LGPL - original licence link has changed is not relivant.
17504  *
17505  * Fork - LGPL
17506  * <script type="text/javascript">
17507  */
17508  
17509 /**
17510  * @class Roo.form.DateField
17511  * @extends Roo.form.TriggerField
17512  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17513 * @constructor
17514 * Create a new DateField
17515 * @param {Object} config
17516  */
17517 Roo.form.DateField = function(config)
17518 {
17519     Roo.form.DateField.superclass.constructor.call(this, config);
17520     
17521       this.addEvents({
17522          
17523         /**
17524          * @event select
17525          * Fires when a date is selected
17526              * @param {Roo.form.DateField} combo This combo box
17527              * @param {Date} date The date selected
17528              */
17529         'select' : true
17530          
17531     });
17532     
17533     
17534     if(typeof this.minValue == "string") {
17535         this.minValue = this.parseDate(this.minValue);
17536     }
17537     if(typeof this.maxValue == "string") {
17538         this.maxValue = this.parseDate(this.maxValue);
17539     }
17540     this.ddMatch = null;
17541     if(this.disabledDates){
17542         var dd = this.disabledDates;
17543         var re = "(?:";
17544         for(var i = 0; i < dd.length; i++){
17545             re += dd[i];
17546             if(i != dd.length-1) {
17547                 re += "|";
17548             }
17549         }
17550         this.ddMatch = new RegExp(re + ")");
17551     }
17552 };
17553
17554 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17555     /**
17556      * @cfg {String} format
17557      * The default date format string which can be overriden for localization support.  The format must be
17558      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17559      */
17560     format : "m/d/y",
17561     /**
17562      * @cfg {String} altFormats
17563      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17564      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17565      */
17566     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17567     /**
17568      * @cfg {Array} disabledDays
17569      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17570      */
17571     disabledDays : null,
17572     /**
17573      * @cfg {String} disabledDaysText
17574      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17575      */
17576     disabledDaysText : "Disabled",
17577     /**
17578      * @cfg {Array} disabledDates
17579      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17580      * expression so they are very powerful. Some examples:
17581      * <ul>
17582      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17583      * <li>["03/08", "09/16"] would disable those days for every year</li>
17584      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17585      * <li>["03/../2006"] would disable every day in March 2006</li>
17586      * <li>["^03"] would disable every day in every March</li>
17587      * </ul>
17588      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17589      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17590      */
17591     disabledDates : null,
17592     /**
17593      * @cfg {String} disabledDatesText
17594      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17595      */
17596     disabledDatesText : "Disabled",
17597         
17598         
17599         /**
17600      * @cfg {Date/String} zeroValue
17601      * if the date is less that this number, then the field is rendered as empty
17602      * default is 1800
17603      */
17604         zeroValue : '1800-01-01',
17605         
17606         
17607     /**
17608      * @cfg {Date/String} minValue
17609      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17610      * valid format (defaults to null).
17611      */
17612     minValue : null,
17613     /**
17614      * @cfg {Date/String} maxValue
17615      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17616      * valid format (defaults to null).
17617      */
17618     maxValue : null,
17619     /**
17620      * @cfg {String} minText
17621      * The error text to display when the date in the cell is before minValue (defaults to
17622      * 'The date in this field must be after {minValue}').
17623      */
17624     minText : "The date in this field must be equal to or after {0}",
17625     /**
17626      * @cfg {String} maxText
17627      * The error text to display when the date in the cell is after maxValue (defaults to
17628      * 'The date in this field must be before {maxValue}').
17629      */
17630     maxText : "The date in this field must be equal to or before {0}",
17631     /**
17632      * @cfg {String} invalidText
17633      * The error text to display when the date in the field is invalid (defaults to
17634      * '{value} is not a valid date - it must be in the format {format}').
17635      */
17636     invalidText : "{0} is not a valid date - it must be in the format {1}",
17637     /**
17638      * @cfg {String} triggerClass
17639      * An additional CSS class used to style the trigger button.  The trigger will always get the
17640      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17641      * which displays a calendar icon).
17642      */
17643     triggerClass : 'x-form-date-trigger',
17644     
17645
17646     /**
17647      * @cfg {Boolean} useIso
17648      * if enabled, then the date field will use a hidden field to store the 
17649      * real value as iso formated date. default (false)
17650      */ 
17651     useIso : false,
17652     /**
17653      * @cfg {String/Object} autoCreate
17654      * A DomHelper element spec, or true for a default element spec (defaults to
17655      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17656      */ 
17657     // private
17658     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17659     
17660     // private
17661     hiddenField: false,
17662     
17663     onRender : function(ct, position)
17664     {
17665         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17666         if (this.useIso) {
17667             //this.el.dom.removeAttribute('name'); 
17668             Roo.log("Changing name?");
17669             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17670             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17671                     'before', true);
17672             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17673             // prevent input submission
17674             this.hiddenName = this.name;
17675         }
17676             
17677             
17678     },
17679     
17680     // private
17681     validateValue : function(value)
17682     {
17683         value = this.formatDate(value);
17684         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17685             Roo.log('super failed');
17686             return false;
17687         }
17688         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17689              return true;
17690         }
17691         var svalue = value;
17692         value = this.parseDate(value);
17693         if(!value){
17694             Roo.log('parse date failed' + svalue);
17695             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17696             return false;
17697         }
17698         var time = value.getTime();
17699         if(this.minValue && time < this.minValue.getTime()){
17700             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17701             return false;
17702         }
17703         if(this.maxValue && time > this.maxValue.getTime()){
17704             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17705             return false;
17706         }
17707         if(this.disabledDays){
17708             var day = value.getDay();
17709             for(var i = 0; i < this.disabledDays.length; i++) {
17710                 if(day === this.disabledDays[i]){
17711                     this.markInvalid(this.disabledDaysText);
17712                     return false;
17713                 }
17714             }
17715         }
17716         var fvalue = this.formatDate(value);
17717         if(this.ddMatch && this.ddMatch.test(fvalue)){
17718             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17719             return false;
17720         }
17721         return true;
17722     },
17723
17724     // private
17725     // Provides logic to override the default TriggerField.validateBlur which just returns true
17726     validateBlur : function(){
17727         return !this.menu || !this.menu.isVisible();
17728     },
17729     
17730     getName: function()
17731     {
17732         // returns hidden if it's set..
17733         if (!this.rendered) {return ''};
17734         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17735         
17736     },
17737
17738     /**
17739      * Returns the current date value of the date field.
17740      * @return {Date} The date value
17741      */
17742     getValue : function(){
17743         
17744         return  this.hiddenField ?
17745                 this.hiddenField.value :
17746                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17747     },
17748
17749     /**
17750      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17751      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17752      * (the default format used is "m/d/y").
17753      * <br />Usage:
17754      * <pre><code>
17755 //All of these calls set the same date value (May 4, 2006)
17756
17757 //Pass a date object:
17758 var dt = new Date('5/4/06');
17759 dateField.setValue(dt);
17760
17761 //Pass a date string (default format):
17762 dateField.setValue('5/4/06');
17763
17764 //Pass a date string (custom format):
17765 dateField.format = 'Y-m-d';
17766 dateField.setValue('2006-5-4');
17767 </code></pre>
17768      * @param {String/Date} date The date or valid date string
17769      */
17770     setValue : function(date){
17771         if (this.hiddenField) {
17772             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17773         }
17774         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17775         // make sure the value field is always stored as a date..
17776         this.value = this.parseDate(date);
17777         
17778         
17779     },
17780
17781     // private
17782     parseDate : function(value){
17783                 
17784                 if (value instanceof Date) {
17785                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17786                                 return  '';
17787                         }
17788                         return value;
17789                 }
17790                 
17791                 
17792         if(!value || value instanceof Date){
17793             return value;
17794         }
17795         var v = Date.parseDate(value, this.format);
17796          if (!v && this.useIso) {
17797             v = Date.parseDate(value, 'Y-m-d');
17798         }
17799         if(!v && this.altFormats){
17800             if(!this.altFormatsArray){
17801                 this.altFormatsArray = this.altFormats.split("|");
17802             }
17803             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17804                 v = Date.parseDate(value, this.altFormatsArray[i]);
17805             }
17806         }
17807                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17808                         v = '';
17809                 }
17810         return v;
17811     },
17812
17813     // private
17814     formatDate : function(date, fmt){
17815         return (!date || !(date instanceof Date)) ?
17816                date : date.dateFormat(fmt || this.format);
17817     },
17818
17819     // private
17820     menuListeners : {
17821         select: function(m, d){
17822             
17823             this.setValue(d);
17824             this.fireEvent('select', this, d);
17825         },
17826         show : function(){ // retain focus styling
17827             this.onFocus();
17828         },
17829         hide : function(){
17830             this.focus.defer(10, this);
17831             var ml = this.menuListeners;
17832             this.menu.un("select", ml.select,  this);
17833             this.menu.un("show", ml.show,  this);
17834             this.menu.un("hide", ml.hide,  this);
17835         }
17836     },
17837
17838     // private
17839     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17840     onTriggerClick : function(){
17841         if(this.disabled){
17842             return;
17843         }
17844         if(this.menu == null){
17845             this.menu = new Roo.menu.DateMenu();
17846         }
17847         Roo.apply(this.menu.picker,  {
17848             showClear: this.allowBlank,
17849             minDate : this.minValue,
17850             maxDate : this.maxValue,
17851             disabledDatesRE : this.ddMatch,
17852             disabledDatesText : this.disabledDatesText,
17853             disabledDays : this.disabledDays,
17854             disabledDaysText : this.disabledDaysText,
17855             format : this.useIso ? 'Y-m-d' : this.format,
17856             minText : String.format(this.minText, this.formatDate(this.minValue)),
17857             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17858         });
17859         this.menu.on(Roo.apply({}, this.menuListeners, {
17860             scope:this
17861         }));
17862         this.menu.picker.setValue(this.getValue() || new Date());
17863         this.menu.show(this.el, "tl-bl?");
17864     },
17865
17866     beforeBlur : function(){
17867         var v = this.parseDate(this.getRawValue());
17868         if(v){
17869             this.setValue(v);
17870         }
17871     },
17872
17873     /*@
17874      * overide
17875      * 
17876      */
17877     isDirty : function() {
17878         if(this.disabled) {
17879             return false;
17880         }
17881         
17882         if(typeof(this.startValue) === 'undefined'){
17883             return false;
17884         }
17885         
17886         return String(this.getValue()) !== String(this.startValue);
17887         
17888     },
17889     // @overide
17890     cleanLeadingSpace : function(e)
17891     {
17892        return;
17893     }
17894     
17895 });/*
17896  * Based on:
17897  * Ext JS Library 1.1.1
17898  * Copyright(c) 2006-2007, Ext JS, LLC.
17899  *
17900  * Originally Released Under LGPL - original licence link has changed is not relivant.
17901  *
17902  * Fork - LGPL
17903  * <script type="text/javascript">
17904  */
17905  
17906 /**
17907  * @class Roo.form.MonthField
17908  * @extends Roo.form.TriggerField
17909  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17910 * @constructor
17911 * Create a new MonthField
17912 * @param {Object} config
17913  */
17914 Roo.form.MonthField = function(config){
17915     
17916     Roo.form.MonthField.superclass.constructor.call(this, config);
17917     
17918       this.addEvents({
17919          
17920         /**
17921          * @event select
17922          * Fires when a date is selected
17923              * @param {Roo.form.MonthFieeld} combo This combo box
17924              * @param {Date} date The date selected
17925              */
17926         'select' : true
17927          
17928     });
17929     
17930     
17931     if(typeof this.minValue == "string") {
17932         this.minValue = this.parseDate(this.minValue);
17933     }
17934     if(typeof this.maxValue == "string") {
17935         this.maxValue = this.parseDate(this.maxValue);
17936     }
17937     this.ddMatch = null;
17938     if(this.disabledDates){
17939         var dd = this.disabledDates;
17940         var re = "(?:";
17941         for(var i = 0; i < dd.length; i++){
17942             re += dd[i];
17943             if(i != dd.length-1) {
17944                 re += "|";
17945             }
17946         }
17947         this.ddMatch = new RegExp(re + ")");
17948     }
17949 };
17950
17951 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17952     /**
17953      * @cfg {String} format
17954      * The default date format string which can be overriden for localization support.  The format must be
17955      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17956      */
17957     format : "M Y",
17958     /**
17959      * @cfg {String} altFormats
17960      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17961      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17962      */
17963     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17964     /**
17965      * @cfg {Array} disabledDays
17966      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17967      */
17968     disabledDays : [0,1,2,3,4,5,6],
17969     /**
17970      * @cfg {String} disabledDaysText
17971      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17972      */
17973     disabledDaysText : "Disabled",
17974     /**
17975      * @cfg {Array} disabledDates
17976      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17977      * expression so they are very powerful. Some examples:
17978      * <ul>
17979      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17980      * <li>["03/08", "09/16"] would disable those days for every year</li>
17981      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17982      * <li>["03/../2006"] would disable every day in March 2006</li>
17983      * <li>["^03"] would disable every day in every March</li>
17984      * </ul>
17985      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17986      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17987      */
17988     disabledDates : null,
17989     /**
17990      * @cfg {String} disabledDatesText
17991      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17992      */
17993     disabledDatesText : "Disabled",
17994     /**
17995      * @cfg {Date/String} minValue
17996      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17997      * valid format (defaults to null).
17998      */
17999     minValue : null,
18000     /**
18001      * @cfg {Date/String} maxValue
18002      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18003      * valid format (defaults to null).
18004      */
18005     maxValue : null,
18006     /**
18007      * @cfg {String} minText
18008      * The error text to display when the date in the cell is before minValue (defaults to
18009      * 'The date in this field must be after {minValue}').
18010      */
18011     minText : "The date in this field must be equal to or after {0}",
18012     /**
18013      * @cfg {String} maxTextf
18014      * The error text to display when the date in the cell is after maxValue (defaults to
18015      * 'The date in this field must be before {maxValue}').
18016      */
18017     maxText : "The date in this field must be equal to or before {0}",
18018     /**
18019      * @cfg {String} invalidText
18020      * The error text to display when the date in the field is invalid (defaults to
18021      * '{value} is not a valid date - it must be in the format {format}').
18022      */
18023     invalidText : "{0} is not a valid date - it must be in the format {1}",
18024     /**
18025      * @cfg {String} triggerClass
18026      * An additional CSS class used to style the trigger button.  The trigger will always get the
18027      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18028      * which displays a calendar icon).
18029      */
18030     triggerClass : 'x-form-date-trigger',
18031     
18032
18033     /**
18034      * @cfg {Boolean} useIso
18035      * if enabled, then the date field will use a hidden field to store the 
18036      * real value as iso formated date. default (true)
18037      */ 
18038     useIso : true,
18039     /**
18040      * @cfg {String/Object} autoCreate
18041      * A DomHelper element spec, or true for a default element spec (defaults to
18042      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18043      */ 
18044     // private
18045     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18046     
18047     // private
18048     hiddenField: false,
18049     
18050     hideMonthPicker : false,
18051     
18052     onRender : function(ct, position)
18053     {
18054         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18055         if (this.useIso) {
18056             this.el.dom.removeAttribute('name'); 
18057             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18058                     'before', true);
18059             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18060             // prevent input submission
18061             this.hiddenName = this.name;
18062         }
18063             
18064             
18065     },
18066     
18067     // private
18068     validateValue : function(value)
18069     {
18070         value = this.formatDate(value);
18071         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18072             return false;
18073         }
18074         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18075              return true;
18076         }
18077         var svalue = value;
18078         value = this.parseDate(value);
18079         if(!value){
18080             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18081             return false;
18082         }
18083         var time = value.getTime();
18084         if(this.minValue && time < this.minValue.getTime()){
18085             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18086             return false;
18087         }
18088         if(this.maxValue && time > this.maxValue.getTime()){
18089             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18090             return false;
18091         }
18092         /*if(this.disabledDays){
18093             var day = value.getDay();
18094             for(var i = 0; i < this.disabledDays.length; i++) {
18095                 if(day === this.disabledDays[i]){
18096                     this.markInvalid(this.disabledDaysText);
18097                     return false;
18098                 }
18099             }
18100         }
18101         */
18102         var fvalue = this.formatDate(value);
18103         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18104             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18105             return false;
18106         }
18107         */
18108         return true;
18109     },
18110
18111     // private
18112     // Provides logic to override the default TriggerField.validateBlur which just returns true
18113     validateBlur : function(){
18114         return !this.menu || !this.menu.isVisible();
18115     },
18116
18117     /**
18118      * Returns the current date value of the date field.
18119      * @return {Date} The date value
18120      */
18121     getValue : function(){
18122         
18123         
18124         
18125         return  this.hiddenField ?
18126                 this.hiddenField.value :
18127                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18128     },
18129
18130     /**
18131      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18132      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18133      * (the default format used is "m/d/y").
18134      * <br />Usage:
18135      * <pre><code>
18136 //All of these calls set the same date value (May 4, 2006)
18137
18138 //Pass a date object:
18139 var dt = new Date('5/4/06');
18140 monthField.setValue(dt);
18141
18142 //Pass a date string (default format):
18143 monthField.setValue('5/4/06');
18144
18145 //Pass a date string (custom format):
18146 monthField.format = 'Y-m-d';
18147 monthField.setValue('2006-5-4');
18148 </code></pre>
18149      * @param {String/Date} date The date or valid date string
18150      */
18151     setValue : function(date){
18152         Roo.log('month setValue' + date);
18153         // can only be first of month..
18154         
18155         var val = this.parseDate(date);
18156         
18157         if (this.hiddenField) {
18158             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18159         }
18160         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18161         this.value = this.parseDate(date);
18162     },
18163
18164     // private
18165     parseDate : function(value){
18166         if(!value || value instanceof Date){
18167             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18168             return value;
18169         }
18170         var v = Date.parseDate(value, this.format);
18171         if (!v && this.useIso) {
18172             v = Date.parseDate(value, 'Y-m-d');
18173         }
18174         if (v) {
18175             // 
18176             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18177         }
18178         
18179         
18180         if(!v && this.altFormats){
18181             if(!this.altFormatsArray){
18182                 this.altFormatsArray = this.altFormats.split("|");
18183             }
18184             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18185                 v = Date.parseDate(value, this.altFormatsArray[i]);
18186             }
18187         }
18188         return v;
18189     },
18190
18191     // private
18192     formatDate : function(date, fmt){
18193         return (!date || !(date instanceof Date)) ?
18194                date : date.dateFormat(fmt || this.format);
18195     },
18196
18197     // private
18198     menuListeners : {
18199         select: function(m, d){
18200             this.setValue(d);
18201             this.fireEvent('select', this, d);
18202         },
18203         show : function(){ // retain focus styling
18204             this.onFocus();
18205         },
18206         hide : function(){
18207             this.focus.defer(10, this);
18208             var ml = this.menuListeners;
18209             this.menu.un("select", ml.select,  this);
18210             this.menu.un("show", ml.show,  this);
18211             this.menu.un("hide", ml.hide,  this);
18212         }
18213     },
18214     // private
18215     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18216     onTriggerClick : function(){
18217         if(this.disabled){
18218             return;
18219         }
18220         if(this.menu == null){
18221             this.menu = new Roo.menu.DateMenu();
18222            
18223         }
18224         
18225         Roo.apply(this.menu.picker,  {
18226             
18227             showClear: this.allowBlank,
18228             minDate : this.minValue,
18229             maxDate : this.maxValue,
18230             disabledDatesRE : this.ddMatch,
18231             disabledDatesText : this.disabledDatesText,
18232             
18233             format : this.useIso ? 'Y-m-d' : this.format,
18234             minText : String.format(this.minText, this.formatDate(this.minValue)),
18235             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18236             
18237         });
18238          this.menu.on(Roo.apply({}, this.menuListeners, {
18239             scope:this
18240         }));
18241        
18242         
18243         var m = this.menu;
18244         var p = m.picker;
18245         
18246         // hide month picker get's called when we called by 'before hide';
18247         
18248         var ignorehide = true;
18249         p.hideMonthPicker  = function(disableAnim){
18250             if (ignorehide) {
18251                 return;
18252             }
18253              if(this.monthPicker){
18254                 Roo.log("hideMonthPicker called");
18255                 if(disableAnim === true){
18256                     this.monthPicker.hide();
18257                 }else{
18258                     this.monthPicker.slideOut('t', {duration:.2});
18259                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18260                     p.fireEvent("select", this, this.value);
18261                     m.hide();
18262                 }
18263             }
18264         }
18265         
18266         Roo.log('picker set value');
18267         Roo.log(this.getValue());
18268         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18269         m.show(this.el, 'tl-bl?');
18270         ignorehide  = false;
18271         // this will trigger hideMonthPicker..
18272         
18273         
18274         // hidden the day picker
18275         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18276         
18277         
18278         
18279       
18280         
18281         p.showMonthPicker.defer(100, p);
18282     
18283         
18284        
18285     },
18286
18287     beforeBlur : function(){
18288         var v = this.parseDate(this.getRawValue());
18289         if(v){
18290             this.setValue(v);
18291         }
18292     }
18293
18294     /** @cfg {Boolean} grow @hide */
18295     /** @cfg {Number} growMin @hide */
18296     /** @cfg {Number} growMax @hide */
18297     /**
18298      * @hide
18299      * @method autoSize
18300      */
18301 });/*
18302  * Based on:
18303  * Ext JS Library 1.1.1
18304  * Copyright(c) 2006-2007, Ext JS, LLC.
18305  *
18306  * Originally Released Under LGPL - original licence link has changed is not relivant.
18307  *
18308  * Fork - LGPL
18309  * <script type="text/javascript">
18310  */
18311  
18312
18313 /**
18314  * @class Roo.form.ComboBox
18315  * @extends Roo.form.TriggerField
18316  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18317  * @constructor
18318  * Create a new ComboBox.
18319  * @param {Object} config Configuration options
18320  */
18321 Roo.form.ComboBox = function(config){
18322     Roo.form.ComboBox.superclass.constructor.call(this, config);
18323     this.addEvents({
18324         /**
18325          * @event expand
18326          * Fires when the dropdown list is expanded
18327              * @param {Roo.form.ComboBox} combo This combo box
18328              */
18329         'expand' : true,
18330         /**
18331          * @event collapse
18332          * Fires when the dropdown list is collapsed
18333              * @param {Roo.form.ComboBox} combo This combo box
18334              */
18335         'collapse' : true,
18336         /**
18337          * @event beforeselect
18338          * Fires before a list item is selected. Return false to cancel the selection.
18339              * @param {Roo.form.ComboBox} combo This combo box
18340              * @param {Roo.data.Record} record The data record returned from the underlying store
18341              * @param {Number} index The index of the selected item in the dropdown list
18342              */
18343         'beforeselect' : true,
18344         /**
18345          * @event select
18346          * Fires when a list item is selected
18347              * @param {Roo.form.ComboBox} combo This combo box
18348              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18349              * @param {Number} index The index of the selected item in the dropdown list
18350              */
18351         'select' : true,
18352         /**
18353          * @event beforequery
18354          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18355          * The event object passed has these properties:
18356              * @param {Roo.form.ComboBox} combo This combo box
18357              * @param {String} query The query
18358              * @param {Boolean} forceAll true to force "all" query
18359              * @param {Boolean} cancel true to cancel the query
18360              * @param {Object} e The query event object
18361              */
18362         'beforequery': true,
18363          /**
18364          * @event add
18365          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18366              * @param {Roo.form.ComboBox} combo This combo box
18367              */
18368         'add' : true,
18369         /**
18370          * @event edit
18371          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18372              * @param {Roo.form.ComboBox} combo This combo box
18373              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18374              */
18375         'edit' : true
18376         
18377         
18378     });
18379     if(this.transform){
18380         this.allowDomMove = false;
18381         var s = Roo.getDom(this.transform);
18382         if(!this.hiddenName){
18383             this.hiddenName = s.name;
18384         }
18385         if(!this.store){
18386             this.mode = 'local';
18387             var d = [], opts = s.options;
18388             for(var i = 0, len = opts.length;i < len; i++){
18389                 var o = opts[i];
18390                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18391                 if(o.selected) {
18392                     this.value = value;
18393                 }
18394                 d.push([value, o.text]);
18395             }
18396             this.store = new Roo.data.SimpleStore({
18397                 'id': 0,
18398                 fields: ['value', 'text'],
18399                 data : d
18400             });
18401             this.valueField = 'value';
18402             this.displayField = 'text';
18403         }
18404         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18405         if(!this.lazyRender){
18406             this.target = true;
18407             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18408             s.parentNode.removeChild(s); // remove it
18409             this.render(this.el.parentNode);
18410         }else{
18411             s.parentNode.removeChild(s); // remove it
18412         }
18413
18414     }
18415     if (this.store) {
18416         this.store = Roo.factory(this.store, Roo.data);
18417     }
18418     
18419     this.selectedIndex = -1;
18420     if(this.mode == 'local'){
18421         if(config.queryDelay === undefined){
18422             this.queryDelay = 10;
18423         }
18424         if(config.minChars === undefined){
18425             this.minChars = 0;
18426         }
18427     }
18428 };
18429
18430 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18431     /**
18432      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18433      */
18434     /**
18435      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18436      * rendering into an Roo.Editor, defaults to false)
18437      */
18438     /**
18439      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18440      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18441      */
18442     /**
18443      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18444      */
18445     /**
18446      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18447      * the dropdown list (defaults to undefined, with no header element)
18448      */
18449
18450      /**
18451      * @cfg {String/Roo.Template} tpl The template to use to render the output
18452      */
18453      
18454     // private
18455     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18456     /**
18457      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18458      */
18459     listWidth: undefined,
18460     /**
18461      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18462      * mode = 'remote' or 'text' if mode = 'local')
18463      */
18464     displayField: undefined,
18465     /**
18466      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18467      * mode = 'remote' or 'value' if mode = 'local'). 
18468      * Note: use of a valueField requires the user make a selection
18469      * in order for a value to be mapped.
18470      */
18471     valueField: undefined,
18472     
18473     
18474     /**
18475      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18476      * field's data value (defaults to the underlying DOM element's name)
18477      */
18478     hiddenName: undefined,
18479     /**
18480      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18481      */
18482     listClass: '',
18483     /**
18484      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18485      */
18486     selectedClass: 'x-combo-selected',
18487     /**
18488      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18489      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18490      * which displays a downward arrow icon).
18491      */
18492     triggerClass : 'x-form-arrow-trigger',
18493     /**
18494      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18495      */
18496     shadow:'sides',
18497     /**
18498      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18499      * anchor positions (defaults to 'tl-bl')
18500      */
18501     listAlign: 'tl-bl?',
18502     /**
18503      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18504      */
18505     maxHeight: 300,
18506     /**
18507      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18508      * query specified by the allQuery config option (defaults to 'query')
18509      */
18510     triggerAction: 'query',
18511     /**
18512      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18513      * (defaults to 4, does not apply if editable = false)
18514      */
18515     minChars : 4,
18516     /**
18517      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18518      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18519      */
18520     typeAhead: false,
18521     /**
18522      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18523      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18524      */
18525     queryDelay: 500,
18526     /**
18527      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18528      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18529      */
18530     pageSize: 0,
18531     /**
18532      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18533      * when editable = true (defaults to false)
18534      */
18535     selectOnFocus:false,
18536     /**
18537      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18538      */
18539     queryParam: 'query',
18540     /**
18541      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18542      * when mode = 'remote' (defaults to 'Loading...')
18543      */
18544     loadingText: 'Loading...',
18545     /**
18546      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18547      */
18548     resizable: false,
18549     /**
18550      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18551      */
18552     handleHeight : 8,
18553     /**
18554      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18555      * traditional select (defaults to true)
18556      */
18557     editable: true,
18558     /**
18559      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18560      */
18561     allQuery: '',
18562     /**
18563      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18564      */
18565     mode: 'remote',
18566     /**
18567      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18568      * listWidth has a higher value)
18569      */
18570     minListWidth : 70,
18571     /**
18572      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18573      * allow the user to set arbitrary text into the field (defaults to false)
18574      */
18575     forceSelection:false,
18576     /**
18577      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18578      * if typeAhead = true (defaults to 250)
18579      */
18580     typeAheadDelay : 250,
18581     /**
18582      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18583      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18584      */
18585     valueNotFoundText : undefined,
18586     /**
18587      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18588      */
18589     blockFocus : false,
18590     
18591     /**
18592      * @cfg {Boolean} disableClear Disable showing of clear button.
18593      */
18594     disableClear : false,
18595     /**
18596      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18597      */
18598     alwaysQuery : false,
18599     
18600     //private
18601     addicon : false,
18602     editicon: false,
18603     
18604     // element that contains real text value.. (when hidden is used..)
18605      
18606     // private
18607     onRender : function(ct, position)
18608     {
18609         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18610         
18611         if(this.hiddenName){
18612             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18613                     'before', true);
18614             this.hiddenField.value =
18615                 this.hiddenValue !== undefined ? this.hiddenValue :
18616                 this.value !== undefined ? this.value : '';
18617
18618             // prevent input submission
18619             this.el.dom.removeAttribute('name');
18620              
18621              
18622         }
18623         
18624         if(Roo.isGecko){
18625             this.el.dom.setAttribute('autocomplete', 'off');
18626         }
18627
18628         var cls = 'x-combo-list';
18629
18630         this.list = new Roo.Layer({
18631             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18632         });
18633
18634         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18635         this.list.setWidth(lw);
18636         this.list.swallowEvent('mousewheel');
18637         this.assetHeight = 0;
18638
18639         if(this.title){
18640             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18641             this.assetHeight += this.header.getHeight();
18642         }
18643
18644         this.innerList = this.list.createChild({cls:cls+'-inner'});
18645         this.innerList.on('mouseover', this.onViewOver, this);
18646         this.innerList.on('mousemove', this.onViewMove, this);
18647         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18648         
18649         if(this.allowBlank && !this.pageSize && !this.disableClear){
18650             this.footer = this.list.createChild({cls:cls+'-ft'});
18651             this.pageTb = new Roo.Toolbar(this.footer);
18652            
18653         }
18654         if(this.pageSize){
18655             this.footer = this.list.createChild({cls:cls+'-ft'});
18656             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18657                     {pageSize: this.pageSize});
18658             
18659         }
18660         
18661         if (this.pageTb && this.allowBlank && !this.disableClear) {
18662             var _this = this;
18663             this.pageTb.add(new Roo.Toolbar.Fill(), {
18664                 cls: 'x-btn-icon x-btn-clear',
18665                 text: '&#160;',
18666                 handler: function()
18667                 {
18668                     _this.collapse();
18669                     _this.clearValue();
18670                     _this.onSelect(false, -1);
18671                 }
18672             });
18673         }
18674         if (this.footer) {
18675             this.assetHeight += this.footer.getHeight();
18676         }
18677         
18678
18679         if(!this.tpl){
18680             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18681         }
18682
18683         this.view = new Roo.View(this.innerList, this.tpl, {
18684             singleSelect:true,
18685             store: this.store,
18686             selectedClass: this.selectedClass
18687         });
18688
18689         this.view.on('click', this.onViewClick, this);
18690
18691         this.store.on('beforeload', this.onBeforeLoad, this);
18692         this.store.on('load', this.onLoad, this);
18693         this.store.on('loadexception', this.onLoadException, this);
18694
18695         if(this.resizable){
18696             this.resizer = new Roo.Resizable(this.list,  {
18697                pinned:true, handles:'se'
18698             });
18699             this.resizer.on('resize', function(r, w, h){
18700                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18701                 this.listWidth = w;
18702                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18703                 this.restrictHeight();
18704             }, this);
18705             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18706         }
18707         if(!this.editable){
18708             this.editable = true;
18709             this.setEditable(false);
18710         }  
18711         
18712         
18713         if (typeof(this.events.add.listeners) != 'undefined') {
18714             
18715             this.addicon = this.wrap.createChild(
18716                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18717        
18718             this.addicon.on('click', function(e) {
18719                 this.fireEvent('add', this);
18720             }, this);
18721         }
18722         if (typeof(this.events.edit.listeners) != 'undefined') {
18723             
18724             this.editicon = this.wrap.createChild(
18725                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18726             if (this.addicon) {
18727                 this.editicon.setStyle('margin-left', '40px');
18728             }
18729             this.editicon.on('click', function(e) {
18730                 
18731                 // we fire even  if inothing is selected..
18732                 this.fireEvent('edit', this, this.lastData );
18733                 
18734             }, this);
18735         }
18736         
18737         
18738         
18739     },
18740
18741     // private
18742     initEvents : function(){
18743         Roo.form.ComboBox.superclass.initEvents.call(this);
18744
18745         this.keyNav = new Roo.KeyNav(this.el, {
18746             "up" : function(e){
18747                 this.inKeyMode = true;
18748                 this.selectPrev();
18749             },
18750
18751             "down" : function(e){
18752                 if(!this.isExpanded()){
18753                     this.onTriggerClick();
18754                 }else{
18755                     this.inKeyMode = true;
18756                     this.selectNext();
18757                 }
18758             },
18759
18760             "enter" : function(e){
18761                 this.onViewClick();
18762                 //return true;
18763             },
18764
18765             "esc" : function(e){
18766                 this.collapse();
18767             },
18768
18769             "tab" : function(e){
18770                 this.onViewClick(false);
18771                 this.fireEvent("specialkey", this, e);
18772                 return true;
18773             },
18774
18775             scope : this,
18776
18777             doRelay : function(foo, bar, hname){
18778                 if(hname == 'down' || this.scope.isExpanded()){
18779                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18780                 }
18781                 return true;
18782             },
18783
18784             forceKeyDown: true
18785         });
18786         this.queryDelay = Math.max(this.queryDelay || 10,
18787                 this.mode == 'local' ? 10 : 250);
18788         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18789         if(this.typeAhead){
18790             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18791         }
18792         if(this.editable !== false){
18793             this.el.on("keyup", this.onKeyUp, this);
18794         }
18795         if(this.forceSelection){
18796             this.on('blur', this.doForce, this);
18797         }
18798     },
18799
18800     onDestroy : function(){
18801         if(this.view){
18802             this.view.setStore(null);
18803             this.view.el.removeAllListeners();
18804             this.view.el.remove();
18805             this.view.purgeListeners();
18806         }
18807         if(this.list){
18808             this.list.destroy();
18809         }
18810         if(this.store){
18811             this.store.un('beforeload', this.onBeforeLoad, this);
18812             this.store.un('load', this.onLoad, this);
18813             this.store.un('loadexception', this.onLoadException, this);
18814         }
18815         Roo.form.ComboBox.superclass.onDestroy.call(this);
18816     },
18817
18818     // private
18819     fireKey : function(e){
18820         if(e.isNavKeyPress() && !this.list.isVisible()){
18821             this.fireEvent("specialkey", this, e);
18822         }
18823     },
18824
18825     // private
18826     onResize: function(w, h){
18827         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18828         
18829         if(typeof w != 'number'){
18830             // we do not handle it!?!?
18831             return;
18832         }
18833         var tw = this.trigger.getWidth();
18834         tw += this.addicon ? this.addicon.getWidth() : 0;
18835         tw += this.editicon ? this.editicon.getWidth() : 0;
18836         var x = w - tw;
18837         this.el.setWidth( this.adjustWidth('input', x));
18838             
18839         this.trigger.setStyle('left', x+'px');
18840         
18841         if(this.list && this.listWidth === undefined){
18842             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18843             this.list.setWidth(lw);
18844             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18845         }
18846         
18847     
18848         
18849     },
18850
18851     /**
18852      * Allow or prevent the user from directly editing the field text.  If false is passed,
18853      * the user will only be able to select from the items defined in the dropdown list.  This method
18854      * is the runtime equivalent of setting the 'editable' config option at config time.
18855      * @param {Boolean} value True to allow the user to directly edit the field text
18856      */
18857     setEditable : function(value){
18858         if(value == this.editable){
18859             return;
18860         }
18861         this.editable = value;
18862         if(!value){
18863             this.el.dom.setAttribute('readOnly', true);
18864             this.el.on('mousedown', this.onTriggerClick,  this);
18865             this.el.addClass('x-combo-noedit');
18866         }else{
18867             this.el.dom.setAttribute('readOnly', false);
18868             this.el.un('mousedown', this.onTriggerClick,  this);
18869             this.el.removeClass('x-combo-noedit');
18870         }
18871     },
18872
18873     // private
18874     onBeforeLoad : function(){
18875         if(!this.hasFocus){
18876             return;
18877         }
18878         this.innerList.update(this.loadingText ?
18879                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18880         this.restrictHeight();
18881         this.selectedIndex = -1;
18882     },
18883
18884     // private
18885     onLoad : function(){
18886         if(!this.hasFocus){
18887             return;
18888         }
18889         if(this.store.getCount() > 0){
18890             this.expand();
18891             this.restrictHeight();
18892             if(this.lastQuery == this.allQuery){
18893                 if(this.editable){
18894                     this.el.dom.select();
18895                 }
18896                 if(!this.selectByValue(this.value, true)){
18897                     this.select(0, true);
18898                 }
18899             }else{
18900                 this.selectNext();
18901                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18902                     this.taTask.delay(this.typeAheadDelay);
18903                 }
18904             }
18905         }else{
18906             this.onEmptyResults();
18907         }
18908         //this.el.focus();
18909     },
18910     // private
18911     onLoadException : function()
18912     {
18913         this.collapse();
18914         Roo.log(this.store.reader.jsonData);
18915         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18916             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18917         }
18918         
18919         
18920     },
18921     // private
18922     onTypeAhead : function(){
18923         if(this.store.getCount() > 0){
18924             var r = this.store.getAt(0);
18925             var newValue = r.data[this.displayField];
18926             var len = newValue.length;
18927             var selStart = this.getRawValue().length;
18928             if(selStart != len){
18929                 this.setRawValue(newValue);
18930                 this.selectText(selStart, newValue.length);
18931             }
18932         }
18933     },
18934
18935     // private
18936     onSelect : function(record, index){
18937         if(this.fireEvent('beforeselect', this, record, index) !== false){
18938             this.setFromData(index > -1 ? record.data : false);
18939             this.collapse();
18940             this.fireEvent('select', this, record, index);
18941         }
18942     },
18943
18944     /**
18945      * Returns the currently selected field value or empty string if no value is set.
18946      * @return {String} value The selected value
18947      */
18948     getValue : function(){
18949         if(this.valueField){
18950             return typeof this.value != 'undefined' ? this.value : '';
18951         }
18952         return Roo.form.ComboBox.superclass.getValue.call(this);
18953     },
18954
18955     /**
18956      * Clears any text/value currently set in the field
18957      */
18958     clearValue : function(){
18959         if(this.hiddenField){
18960             this.hiddenField.value = '';
18961         }
18962         this.value = '';
18963         this.setRawValue('');
18964         this.lastSelectionText = '';
18965         
18966     },
18967
18968     /**
18969      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18970      * will be displayed in the field.  If the value does not match the data value of an existing item,
18971      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18972      * Otherwise the field will be blank (although the value will still be set).
18973      * @param {String} value The value to match
18974      */
18975     setValue : function(v){
18976         var text = v;
18977         if(this.valueField){
18978             var r = this.findRecord(this.valueField, v);
18979             if(r){
18980                 text = r.data[this.displayField];
18981             }else if(this.valueNotFoundText !== undefined){
18982                 text = this.valueNotFoundText;
18983             }
18984         }
18985         this.lastSelectionText = text;
18986         if(this.hiddenField){
18987             this.hiddenField.value = v;
18988         }
18989         Roo.form.ComboBox.superclass.setValue.call(this, text);
18990         this.value = v;
18991     },
18992     /**
18993      * @property {Object} the last set data for the element
18994      */
18995     
18996     lastData : false,
18997     /**
18998      * Sets the value of the field based on a object which is related to the record format for the store.
18999      * @param {Object} value the value to set as. or false on reset?
19000      */
19001     setFromData : function(o){
19002         var dv = ''; // display value
19003         var vv = ''; // value value..
19004         this.lastData = o;
19005         if (this.displayField) {
19006             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19007         } else {
19008             // this is an error condition!!!
19009             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19010         }
19011         
19012         if(this.valueField){
19013             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19014         }
19015         if(this.hiddenField){
19016             this.hiddenField.value = vv;
19017             
19018             this.lastSelectionText = dv;
19019             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19020             this.value = vv;
19021             return;
19022         }
19023         // no hidden field.. - we store the value in 'value', but still display
19024         // display field!!!!
19025         this.lastSelectionText = dv;
19026         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19027         this.value = vv;
19028         
19029         
19030     },
19031     // private
19032     reset : function(){
19033         // overridden so that last data is reset..
19034         this.setValue(this.resetValue);
19035         this.originalValue = this.getValue();
19036         this.clearInvalid();
19037         this.lastData = false;
19038         if (this.view) {
19039             this.view.clearSelections();
19040         }
19041     },
19042     // private
19043     findRecord : function(prop, value){
19044         var record;
19045         if(this.store.getCount() > 0){
19046             this.store.each(function(r){
19047                 if(r.data[prop] == value){
19048                     record = r;
19049                     return false;
19050                 }
19051                 return true;
19052             });
19053         }
19054         return record;
19055     },
19056     
19057     getName: function()
19058     {
19059         // returns hidden if it's set..
19060         if (!this.rendered) {return ''};
19061         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19062         
19063     },
19064     // private
19065     onViewMove : function(e, t){
19066         this.inKeyMode = false;
19067     },
19068
19069     // private
19070     onViewOver : function(e, t){
19071         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19072             return;
19073         }
19074         var item = this.view.findItemFromChild(t);
19075         if(item){
19076             var index = this.view.indexOf(item);
19077             this.select(index, false);
19078         }
19079     },
19080
19081     // private
19082     onViewClick : function(doFocus)
19083     {
19084         var index = this.view.getSelectedIndexes()[0];
19085         var r = this.store.getAt(index);
19086         if(r){
19087             this.onSelect(r, index);
19088         }
19089         if(doFocus !== false && !this.blockFocus){
19090             this.el.focus();
19091         }
19092     },
19093
19094     // private
19095     restrictHeight : function(){
19096         this.innerList.dom.style.height = '';
19097         var inner = this.innerList.dom;
19098         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19099         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19100         this.list.beginUpdate();
19101         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19102         this.list.alignTo(this.el, this.listAlign);
19103         this.list.endUpdate();
19104     },
19105
19106     // private
19107     onEmptyResults : function(){
19108         this.collapse();
19109     },
19110
19111     /**
19112      * Returns true if the dropdown list is expanded, else false.
19113      */
19114     isExpanded : function(){
19115         return this.list.isVisible();
19116     },
19117
19118     /**
19119      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19120      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19121      * @param {String} value The data value of the item to select
19122      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19123      * selected item if it is not currently in view (defaults to true)
19124      * @return {Boolean} True if the value matched an item in the list, else false
19125      */
19126     selectByValue : function(v, scrollIntoView){
19127         if(v !== undefined && v !== null){
19128             var r = this.findRecord(this.valueField || this.displayField, v);
19129             if(r){
19130                 this.select(this.store.indexOf(r), scrollIntoView);
19131                 return true;
19132             }
19133         }
19134         return false;
19135     },
19136
19137     /**
19138      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19139      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19140      * @param {Number} index The zero-based index of the list item to select
19141      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19142      * selected item if it is not currently in view (defaults to true)
19143      */
19144     select : function(index, scrollIntoView){
19145         this.selectedIndex = index;
19146         this.view.select(index);
19147         if(scrollIntoView !== false){
19148             var el = this.view.getNode(index);
19149             if(el){
19150                 this.innerList.scrollChildIntoView(el, false);
19151             }
19152         }
19153     },
19154
19155     // private
19156     selectNext : function(){
19157         var ct = this.store.getCount();
19158         if(ct > 0){
19159             if(this.selectedIndex == -1){
19160                 this.select(0);
19161             }else if(this.selectedIndex < ct-1){
19162                 this.select(this.selectedIndex+1);
19163             }
19164         }
19165     },
19166
19167     // private
19168     selectPrev : function(){
19169         var ct = this.store.getCount();
19170         if(ct > 0){
19171             if(this.selectedIndex == -1){
19172                 this.select(0);
19173             }else if(this.selectedIndex != 0){
19174                 this.select(this.selectedIndex-1);
19175             }
19176         }
19177     },
19178
19179     // private
19180     onKeyUp : function(e){
19181         if(this.editable !== false && !e.isSpecialKey()){
19182             this.lastKey = e.getKey();
19183             this.dqTask.delay(this.queryDelay);
19184         }
19185     },
19186
19187     // private
19188     validateBlur : function(){
19189         return !this.list || !this.list.isVisible();   
19190     },
19191
19192     // private
19193     initQuery : function(){
19194         this.doQuery(this.getRawValue());
19195     },
19196
19197     // private
19198     doForce : function(){
19199         if(this.el.dom.value.length > 0){
19200             this.el.dom.value =
19201                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19202              
19203         }
19204     },
19205
19206     /**
19207      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19208      * query allowing the query action to be canceled if needed.
19209      * @param {String} query The SQL query to execute
19210      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19211      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19212      * saved in the current store (defaults to false)
19213      */
19214     doQuery : function(q, forceAll){
19215         if(q === undefined || q === null){
19216             q = '';
19217         }
19218         var qe = {
19219             query: q,
19220             forceAll: forceAll,
19221             combo: this,
19222             cancel:false
19223         };
19224         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19225             return false;
19226         }
19227         q = qe.query;
19228         forceAll = qe.forceAll;
19229         if(forceAll === true || (q.length >= this.minChars)){
19230             if(this.lastQuery != q || this.alwaysQuery){
19231                 this.lastQuery = q;
19232                 if(this.mode == 'local'){
19233                     this.selectedIndex = -1;
19234                     if(forceAll){
19235                         this.store.clearFilter();
19236                     }else{
19237                         this.store.filter(this.displayField, q);
19238                     }
19239                     this.onLoad();
19240                 }else{
19241                     this.store.baseParams[this.queryParam] = q;
19242                     this.store.load({
19243                         params: this.getParams(q)
19244                     });
19245                     this.expand();
19246                 }
19247             }else{
19248                 this.selectedIndex = -1;
19249                 this.onLoad();   
19250             }
19251         }
19252     },
19253
19254     // private
19255     getParams : function(q){
19256         var p = {};
19257         //p[this.queryParam] = q;
19258         if(this.pageSize){
19259             p.start = 0;
19260             p.limit = this.pageSize;
19261         }
19262         return p;
19263     },
19264
19265     /**
19266      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19267      */
19268     collapse : function(){
19269         if(!this.isExpanded()){
19270             return;
19271         }
19272         this.list.hide();
19273         Roo.get(document).un('mousedown', this.collapseIf, this);
19274         Roo.get(document).un('mousewheel', this.collapseIf, this);
19275         if (!this.editable) {
19276             Roo.get(document).un('keydown', this.listKeyPress, this);
19277         }
19278         this.fireEvent('collapse', this);
19279     },
19280
19281     // private
19282     collapseIf : function(e){
19283         if(!e.within(this.wrap) && !e.within(this.list)){
19284             this.collapse();
19285         }
19286     },
19287
19288     /**
19289      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19290      */
19291     expand : function(){
19292         if(this.isExpanded() || !this.hasFocus){
19293             return;
19294         }
19295         this.list.alignTo(this.el, this.listAlign);
19296         this.list.show();
19297         Roo.get(document).on('mousedown', this.collapseIf, this);
19298         Roo.get(document).on('mousewheel', this.collapseIf, this);
19299         if (!this.editable) {
19300             Roo.get(document).on('keydown', this.listKeyPress, this);
19301         }
19302         
19303         this.fireEvent('expand', this);
19304     },
19305
19306     // private
19307     // Implements the default empty TriggerField.onTriggerClick function
19308     onTriggerClick : function(){
19309         if(this.disabled){
19310             return;
19311         }
19312         if(this.isExpanded()){
19313             this.collapse();
19314             if (!this.blockFocus) {
19315                 this.el.focus();
19316             }
19317             
19318         }else {
19319             this.hasFocus = true;
19320             if(this.triggerAction == 'all') {
19321                 this.doQuery(this.allQuery, true);
19322             } else {
19323                 this.doQuery(this.getRawValue());
19324             }
19325             if (!this.blockFocus) {
19326                 this.el.focus();
19327             }
19328         }
19329     },
19330     listKeyPress : function(e)
19331     {
19332         //Roo.log('listkeypress');
19333         // scroll to first matching element based on key pres..
19334         if (e.isSpecialKey()) {
19335             return false;
19336         }
19337         var k = String.fromCharCode(e.getKey()).toUpperCase();
19338         //Roo.log(k);
19339         var match  = false;
19340         var csel = this.view.getSelectedNodes();
19341         var cselitem = false;
19342         if (csel.length) {
19343             var ix = this.view.indexOf(csel[0]);
19344             cselitem  = this.store.getAt(ix);
19345             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19346                 cselitem = false;
19347             }
19348             
19349         }
19350         
19351         this.store.each(function(v) { 
19352             if (cselitem) {
19353                 // start at existing selection.
19354                 if (cselitem.id == v.id) {
19355                     cselitem = false;
19356                 }
19357                 return;
19358             }
19359                 
19360             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19361                 match = this.store.indexOf(v);
19362                 return false;
19363             }
19364         }, this);
19365         
19366         if (match === false) {
19367             return true; // no more action?
19368         }
19369         // scroll to?
19370         this.view.select(match);
19371         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19372         sn.scrollIntoView(sn.dom.parentNode, false);
19373     } 
19374
19375     /** 
19376     * @cfg {Boolean} grow 
19377     * @hide 
19378     */
19379     /** 
19380     * @cfg {Number} growMin 
19381     * @hide 
19382     */
19383     /** 
19384     * @cfg {Number} growMax 
19385     * @hide 
19386     */
19387     /**
19388      * @hide
19389      * @method autoSize
19390      */
19391 });/*
19392  * Copyright(c) 2010-2012, Roo J Solutions Limited
19393  *
19394  * Licence LGPL
19395  *
19396  */
19397
19398 /**
19399  * @class Roo.form.ComboBoxArray
19400  * @extends Roo.form.TextField
19401  * A facebook style adder... for lists of email / people / countries  etc...
19402  * pick multiple items from a combo box, and shows each one.
19403  *
19404  *  Fred [x]  Brian [x]  [Pick another |v]
19405  *
19406  *
19407  *  For this to work: it needs various extra information
19408  *    - normal combo problay has
19409  *      name, hiddenName
19410  *    + displayField, valueField
19411  *
19412  *    For our purpose...
19413  *
19414  *
19415  *   If we change from 'extends' to wrapping...
19416  *   
19417  *  
19418  *
19419  
19420  
19421  * @constructor
19422  * Create a new ComboBoxArray.
19423  * @param {Object} config Configuration options
19424  */
19425  
19426
19427 Roo.form.ComboBoxArray = function(config)
19428 {
19429     this.addEvents({
19430         /**
19431          * @event beforeremove
19432          * Fires before remove the value from the list
19433              * @param {Roo.form.ComboBoxArray} _self This combo box array
19434              * @param {Roo.form.ComboBoxArray.Item} item removed item
19435              */
19436         'beforeremove' : true,
19437         /**
19438          * @event remove
19439          * Fires when remove the value from the list
19440              * @param {Roo.form.ComboBoxArray} _self This combo box array
19441              * @param {Roo.form.ComboBoxArray.Item} item removed item
19442              */
19443         'remove' : true
19444         
19445         
19446     });
19447     
19448     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19449     
19450     this.items = new Roo.util.MixedCollection(false);
19451     
19452     // construct the child combo...
19453     
19454     
19455     
19456     
19457    
19458     
19459 }
19460
19461  
19462 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19463
19464     /**
19465      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19466      */
19467     
19468     lastData : false,
19469     
19470     // behavies liek a hiddne field
19471     inputType:      'hidden',
19472     /**
19473      * @cfg {Number} width The width of the box that displays the selected element
19474      */ 
19475     width:          300,
19476
19477     
19478     
19479     /**
19480      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19481      */
19482     name : false,
19483     /**
19484      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19485      */
19486     hiddenName : false,
19487       /**
19488      * @cfg {String} seperator    The value seperator normally ',' 
19489      */
19490     seperator : ',',
19491     
19492     // private the array of items that are displayed..
19493     items  : false,
19494     // private - the hidden field el.
19495     hiddenEl : false,
19496     // private - the filed el..
19497     el : false,
19498     
19499     //validateValue : function() { return true; }, // all values are ok!
19500     //onAddClick: function() { },
19501     
19502     onRender : function(ct, position) 
19503     {
19504         
19505         // create the standard hidden element
19506         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19507         
19508         
19509         // give fake names to child combo;
19510         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19511         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19512         
19513         this.combo = Roo.factory(this.combo, Roo.form);
19514         this.combo.onRender(ct, position);
19515         if (typeof(this.combo.width) != 'undefined') {
19516             this.combo.onResize(this.combo.width,0);
19517         }
19518         
19519         this.combo.initEvents();
19520         
19521         // assigned so form know we need to do this..
19522         this.store          = this.combo.store;
19523         this.valueField     = this.combo.valueField;
19524         this.displayField   = this.combo.displayField ;
19525         
19526         
19527         this.combo.wrap.addClass('x-cbarray-grp');
19528         
19529         var cbwrap = this.combo.wrap.createChild(
19530             {tag: 'div', cls: 'x-cbarray-cb'},
19531             this.combo.el.dom
19532         );
19533         
19534              
19535         this.hiddenEl = this.combo.wrap.createChild({
19536             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19537         });
19538         this.el = this.combo.wrap.createChild({
19539             tag: 'input',  type:'hidden' , name: this.name, value : ''
19540         });
19541          //   this.el.dom.removeAttribute("name");
19542         
19543         
19544         this.outerWrap = this.combo.wrap;
19545         this.wrap = cbwrap;
19546         
19547         this.outerWrap.setWidth(this.width);
19548         this.outerWrap.dom.removeChild(this.el.dom);
19549         
19550         this.wrap.dom.appendChild(this.el.dom);
19551         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19552         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19553         
19554         this.combo.trigger.setStyle('position','relative');
19555         this.combo.trigger.setStyle('left', '0px');
19556         this.combo.trigger.setStyle('top', '2px');
19557         
19558         this.combo.el.setStyle('vertical-align', 'text-bottom');
19559         
19560         //this.trigger.setStyle('vertical-align', 'top');
19561         
19562         // this should use the code from combo really... on('add' ....)
19563         if (this.adder) {
19564             
19565         
19566             this.adder = this.outerWrap.createChild(
19567                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19568             var _t = this;
19569             this.adder.on('click', function(e) {
19570                 _t.fireEvent('adderclick', this, e);
19571             }, _t);
19572         }
19573         //var _t = this;
19574         //this.adder.on('click', this.onAddClick, _t);
19575         
19576         
19577         this.combo.on('select', function(cb, rec, ix) {
19578             this.addItem(rec.data);
19579             
19580             cb.setValue('');
19581             cb.el.dom.value = '';
19582             //cb.lastData = rec.data;
19583             // add to list
19584             
19585         }, this);
19586         
19587         
19588     },
19589     
19590     
19591     getName: function()
19592     {
19593         // returns hidden if it's set..
19594         if (!this.rendered) {return ''};
19595         return  this.hiddenName ? this.hiddenName : this.name;
19596         
19597     },
19598     
19599     
19600     onResize: function(w, h){
19601         
19602         return;
19603         // not sure if this is needed..
19604         //this.combo.onResize(w,h);
19605         
19606         if(typeof w != 'number'){
19607             // we do not handle it!?!?
19608             return;
19609         }
19610         var tw = this.combo.trigger.getWidth();
19611         tw += this.addicon ? this.addicon.getWidth() : 0;
19612         tw += this.editicon ? this.editicon.getWidth() : 0;
19613         var x = w - tw;
19614         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19615             
19616         this.combo.trigger.setStyle('left', '0px');
19617         
19618         if(this.list && this.listWidth === undefined){
19619             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19620             this.list.setWidth(lw);
19621             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19622         }
19623         
19624     
19625         
19626     },
19627     
19628     addItem: function(rec)
19629     {
19630         var valueField = this.combo.valueField;
19631         var displayField = this.combo.displayField;
19632         
19633         if (this.items.indexOfKey(rec[valueField]) > -1) {
19634             //console.log("GOT " + rec.data.id);
19635             return;
19636         }
19637         
19638         var x = new Roo.form.ComboBoxArray.Item({
19639             //id : rec[this.idField],
19640             data : rec,
19641             displayField : displayField ,
19642             tipField : displayField ,
19643             cb : this
19644         });
19645         // use the 
19646         this.items.add(rec[valueField],x);
19647         // add it before the element..
19648         this.updateHiddenEl();
19649         x.render(this.outerWrap, this.wrap.dom);
19650         // add the image handler..
19651     },
19652     
19653     updateHiddenEl : function()
19654     {
19655         this.validate();
19656         if (!this.hiddenEl) {
19657             return;
19658         }
19659         var ar = [];
19660         var idField = this.combo.valueField;
19661         
19662         this.items.each(function(f) {
19663             ar.push(f.data[idField]);
19664         });
19665         this.hiddenEl.dom.value = ar.join(this.seperator);
19666         this.validate();
19667     },
19668     
19669     reset : function()
19670     {
19671         this.items.clear();
19672         
19673         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19674            el.remove();
19675         });
19676         
19677         this.el.dom.value = '';
19678         if (this.hiddenEl) {
19679             this.hiddenEl.dom.value = '';
19680         }
19681         
19682     },
19683     getValue: function()
19684     {
19685         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19686     },
19687     setValue: function(v) // not a valid action - must use addItems..
19688     {
19689         
19690         this.reset();
19691          
19692         if (this.store.isLocal && (typeof(v) == 'string')) {
19693             // then we can use the store to find the values..
19694             // comma seperated at present.. this needs to allow JSON based encoding..
19695             this.hiddenEl.value  = v;
19696             var v_ar = [];
19697             Roo.each(v.split(this.seperator), function(k) {
19698                 Roo.log("CHECK " + this.valueField + ',' + k);
19699                 var li = this.store.query(this.valueField, k);
19700                 if (!li.length) {
19701                     return;
19702                 }
19703                 var add = {};
19704                 add[this.valueField] = k;
19705                 add[this.displayField] = li.item(0).data[this.displayField];
19706                 
19707                 this.addItem(add);
19708             }, this) 
19709              
19710         }
19711         if (typeof(v) == 'object' ) {
19712             // then let's assume it's an array of objects..
19713             Roo.each(v, function(l) {
19714                 var add = l;
19715                 if (typeof(l) == 'string') {
19716                     add = {};
19717                     add[this.valueField] = l;
19718                     add[this.displayField] = l
19719                 }
19720                 this.addItem(add);
19721             }, this);
19722              
19723         }
19724         
19725         
19726     },
19727     setFromData: function(v)
19728     {
19729         // this recieves an object, if setValues is called.
19730         this.reset();
19731         this.el.dom.value = v[this.displayField];
19732         this.hiddenEl.dom.value = v[this.valueField];
19733         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19734             return;
19735         }
19736         var kv = v[this.valueField];
19737         var dv = v[this.displayField];
19738         kv = typeof(kv) != 'string' ? '' : kv;
19739         dv = typeof(dv) != 'string' ? '' : dv;
19740         
19741         
19742         var keys = kv.split(this.seperator);
19743         var display = dv.split(this.seperator);
19744         for (var i = 0 ; i < keys.length; i++) {
19745             add = {};
19746             add[this.valueField] = keys[i];
19747             add[this.displayField] = display[i];
19748             this.addItem(add);
19749         }
19750       
19751         
19752     },
19753     
19754     /**
19755      * Validates the combox array value
19756      * @return {Boolean} True if the value is valid, else false
19757      */
19758     validate : function(){
19759         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19760             this.clearInvalid();
19761             return true;
19762         }
19763         return false;
19764     },
19765     
19766     validateValue : function(value){
19767         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19768         
19769     },
19770     
19771     /*@
19772      * overide
19773      * 
19774      */
19775     isDirty : function() {
19776         if(this.disabled) {
19777             return false;
19778         }
19779         
19780         try {
19781             var d = Roo.decode(String(this.originalValue));
19782         } catch (e) {
19783             return String(this.getValue()) !== String(this.originalValue);
19784         }
19785         
19786         var originalValue = [];
19787         
19788         for (var i = 0; i < d.length; i++){
19789             originalValue.push(d[i][this.valueField]);
19790         }
19791         
19792         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19793         
19794     }
19795     
19796 });
19797
19798
19799
19800 /**
19801  * @class Roo.form.ComboBoxArray.Item
19802  * @extends Roo.BoxComponent
19803  * A selected item in the list
19804  *  Fred [x]  Brian [x]  [Pick another |v]
19805  * 
19806  * @constructor
19807  * Create a new item.
19808  * @param {Object} config Configuration options
19809  */
19810  
19811 Roo.form.ComboBoxArray.Item = function(config) {
19812     config.id = Roo.id();
19813     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19814 }
19815
19816 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19817     data : {},
19818     cb: false,
19819     displayField : false,
19820     tipField : false,
19821     
19822     
19823     defaultAutoCreate : {
19824         tag: 'div',
19825         cls: 'x-cbarray-item',
19826         cn : [ 
19827             { tag: 'div' },
19828             {
19829                 tag: 'img',
19830                 width:16,
19831                 height : 16,
19832                 src : Roo.BLANK_IMAGE_URL ,
19833                 align: 'center'
19834             }
19835         ]
19836         
19837     },
19838     
19839  
19840     onRender : function(ct, position)
19841     {
19842         Roo.form.Field.superclass.onRender.call(this, ct, position);
19843         
19844         if(!this.el){
19845             var cfg = this.getAutoCreate();
19846             this.el = ct.createChild(cfg, position);
19847         }
19848         
19849         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19850         
19851         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19852             this.cb.renderer(this.data) :
19853             String.format('{0}',this.data[this.displayField]);
19854         
19855             
19856         this.el.child('div').dom.setAttribute('qtip',
19857                         String.format('{0}',this.data[this.tipField])
19858         );
19859         
19860         this.el.child('img').on('click', this.remove, this);
19861         
19862     },
19863    
19864     remove : function()
19865     {
19866         if(this.cb.disabled){
19867             return;
19868         }
19869         
19870         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19871             this.cb.items.remove(this);
19872             this.el.child('img').un('click', this.remove, this);
19873             this.el.remove();
19874             this.cb.updateHiddenEl();
19875
19876             this.cb.fireEvent('remove', this.cb, this);
19877         }
19878         
19879     }
19880 });/*
19881  * RooJS Library 1.1.1
19882  * Copyright(c) 2008-2011  Alan Knowles
19883  *
19884  * License - LGPL
19885  */
19886  
19887
19888 /**
19889  * @class Roo.form.ComboNested
19890  * @extends Roo.form.ComboBox
19891  * A combobox for that allows selection of nested items in a list,
19892  * eg.
19893  *
19894  *  Book
19895  *    -> red
19896  *    -> green
19897  *  Table
19898  *    -> square
19899  *      ->red
19900  *      ->green
19901  *    -> rectangle
19902  *      ->green
19903  *      
19904  * 
19905  * @constructor
19906  * Create a new ComboNested
19907  * @param {Object} config Configuration options
19908  */
19909 Roo.form.ComboNested = function(config){
19910     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19911     // should verify some data...
19912     // like
19913     // hiddenName = required..
19914     // displayField = required
19915     // valudField == required
19916     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19917     var _t = this;
19918     Roo.each(req, function(e) {
19919         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19920             throw "Roo.form.ComboNested : missing value for: " + e;
19921         }
19922     });
19923      
19924     
19925 };
19926
19927 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19928    
19929     /*
19930      * @config {Number} max Number of columns to show
19931      */
19932     
19933     maxColumns : 3,
19934    
19935     list : null, // the outermost div..
19936     innerLists : null, // the
19937     views : null,
19938     stores : null,
19939     // private
19940     loadingChildren : false,
19941     
19942     onRender : function(ct, position)
19943     {
19944         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19945         
19946         if(this.hiddenName){
19947             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19948                     'before', true);
19949             this.hiddenField.value =
19950                 this.hiddenValue !== undefined ? this.hiddenValue :
19951                 this.value !== undefined ? this.value : '';
19952
19953             // prevent input submission
19954             this.el.dom.removeAttribute('name');
19955              
19956              
19957         }
19958         
19959         if(Roo.isGecko){
19960             this.el.dom.setAttribute('autocomplete', 'off');
19961         }
19962
19963         var cls = 'x-combo-list';
19964
19965         this.list = new Roo.Layer({
19966             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19967         });
19968
19969         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19970         this.list.setWidth(lw);
19971         this.list.swallowEvent('mousewheel');
19972         this.assetHeight = 0;
19973
19974         if(this.title){
19975             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19976             this.assetHeight += this.header.getHeight();
19977         }
19978         this.innerLists = [];
19979         this.views = [];
19980         this.stores = [];
19981         for (var i =0 ; i < this.maxColumns; i++) {
19982             this.onRenderList( cls, i);
19983         }
19984         
19985         // always needs footer, as we are going to have an 'OK' button.
19986         this.footer = this.list.createChild({cls:cls+'-ft'});
19987         this.pageTb = new Roo.Toolbar(this.footer);  
19988         var _this = this;
19989         this.pageTb.add(  {
19990             
19991             text: 'Done',
19992             handler: function()
19993             {
19994                 _this.collapse();
19995             }
19996         });
19997         
19998         if ( this.allowBlank && !this.disableClear) {
19999             
20000             this.pageTb.add(new Roo.Toolbar.Fill(), {
20001                 cls: 'x-btn-icon x-btn-clear',
20002                 text: '&#160;',
20003                 handler: function()
20004                 {
20005                     _this.collapse();
20006                     _this.clearValue();
20007                     _this.onSelect(false, -1);
20008                 }
20009             });
20010         }
20011         if (this.footer) {
20012             this.assetHeight += this.footer.getHeight();
20013         }
20014         
20015     },
20016     onRenderList : function (  cls, i)
20017     {
20018         
20019         var lw = Math.floor(
20020                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20021         );
20022         
20023         this.list.setWidth(lw); // default to '1'
20024
20025         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20026         //il.on('mouseover', this.onViewOver, this, { list:  i });
20027         //il.on('mousemove', this.onViewMove, this, { list:  i });
20028         il.setWidth(lw);
20029         il.setStyle({ 'overflow-x' : 'hidden'});
20030
20031         if(!this.tpl){
20032             this.tpl = new Roo.Template({
20033                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20034                 isEmpty: function (value, allValues) {
20035                     //Roo.log(value);
20036                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20037                     return dl ? 'has-children' : 'no-children'
20038                 }
20039             });
20040         }
20041         
20042         var store  = this.store;
20043         if (i > 0) {
20044             store  = new Roo.data.SimpleStore({
20045                 //fields : this.store.reader.meta.fields,
20046                 reader : this.store.reader,
20047                 data : [ ]
20048             });
20049         }
20050         this.stores[i]  = store;
20051                   
20052         var view = this.views[i] = new Roo.View(
20053             il,
20054             this.tpl,
20055             {
20056                 singleSelect:true,
20057                 store: store,
20058                 selectedClass: this.selectedClass
20059             }
20060         );
20061         view.getEl().setWidth(lw);
20062         view.getEl().setStyle({
20063             position: i < 1 ? 'relative' : 'absolute',
20064             top: 0,
20065             left: (i * lw ) + 'px',
20066             display : i > 0 ? 'none' : 'block'
20067         });
20068         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20069         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20070         //view.on('click', this.onViewClick, this, { list : i });
20071
20072         store.on('beforeload', this.onBeforeLoad, this);
20073         store.on('load',  this.onLoad, this, { list  : i});
20074         store.on('loadexception', this.onLoadException, this);
20075
20076         // hide the other vies..
20077         
20078         
20079         
20080     },
20081       
20082     restrictHeight : function()
20083     {
20084         var mh = 0;
20085         Roo.each(this.innerLists, function(il,i) {
20086             var el = this.views[i].getEl();
20087             el.dom.style.height = '';
20088             var inner = el.dom;
20089             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20090             // only adjust heights on other ones..
20091             mh = Math.max(h, mh);
20092             if (i < 1) {
20093                 
20094                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20095                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20096                
20097             }
20098             
20099             
20100         }, this);
20101         
20102         this.list.beginUpdate();
20103         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20104         this.list.alignTo(this.el, this.listAlign);
20105         this.list.endUpdate();
20106         
20107     },
20108      
20109     
20110     // -- store handlers..
20111     // private
20112     onBeforeLoad : function()
20113     {
20114         if(!this.hasFocus){
20115             return;
20116         }
20117         this.innerLists[0].update(this.loadingText ?
20118                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20119         this.restrictHeight();
20120         this.selectedIndex = -1;
20121     },
20122     // private
20123     onLoad : function(a,b,c,d)
20124     {
20125         if (!this.loadingChildren) {
20126             // then we are loading the top level. - hide the children
20127             for (var i = 1;i < this.views.length; i++) {
20128                 this.views[i].getEl().setStyle({ display : 'none' });
20129             }
20130             var lw = Math.floor(
20131                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20132             );
20133         
20134              this.list.setWidth(lw); // default to '1'
20135
20136             
20137         }
20138         if(!this.hasFocus){
20139             return;
20140         }
20141         
20142         if(this.store.getCount() > 0) {
20143             this.expand();
20144             this.restrictHeight();   
20145         } else {
20146             this.onEmptyResults();
20147         }
20148         
20149         if (!this.loadingChildren) {
20150             this.selectActive();
20151         }
20152         /*
20153         this.stores[1].loadData([]);
20154         this.stores[2].loadData([]);
20155         this.views
20156         */    
20157     
20158         //this.el.focus();
20159     },
20160     
20161     
20162     // private
20163     onLoadException : function()
20164     {
20165         this.collapse();
20166         Roo.log(this.store.reader.jsonData);
20167         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20168             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20169         }
20170         
20171         
20172     },
20173     // no cleaning of leading spaces on blur here.
20174     cleanLeadingSpace : function(e) { },
20175     
20176
20177     onSelectChange : function (view, sels, opts )
20178     {
20179         var ix = view.getSelectedIndexes();
20180          
20181         if (opts.list > this.maxColumns - 2) {
20182             if (view.store.getCount()<  1) {
20183                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20184
20185             } else  {
20186                 if (ix.length) {
20187                     // used to clear ?? but if we are loading unselected 
20188                     this.setFromData(view.store.getAt(ix[0]).data);
20189                 }
20190                 
20191             }
20192             
20193             return;
20194         }
20195         
20196         if (!ix.length) {
20197             // this get's fired when trigger opens..
20198            // this.setFromData({});
20199             var str = this.stores[opts.list+1];
20200             str.data.clear(); // removeall wihtout the fire events..
20201             return;
20202         }
20203         
20204         var rec = view.store.getAt(ix[0]);
20205          
20206         this.setFromData(rec.data);
20207         this.fireEvent('select', this, rec, ix[0]);
20208         
20209         var lw = Math.floor(
20210              (
20211                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20212              ) / this.maxColumns
20213         );
20214         this.loadingChildren = true;
20215         this.stores[opts.list+1].loadDataFromChildren( rec );
20216         this.loadingChildren = false;
20217         var dl = this.stores[opts.list+1]. getTotalCount();
20218         
20219         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20220         
20221         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20222         for (var i = opts.list+2; i < this.views.length;i++) {
20223             this.views[i].getEl().setStyle({ display : 'none' });
20224         }
20225         
20226         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20227         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20228         
20229         if (this.isLoading) {
20230            // this.selectActive(opts.list);
20231         }
20232          
20233     },
20234     
20235     
20236     
20237     
20238     onDoubleClick : function()
20239     {
20240         this.collapse(); //??
20241     },
20242     
20243      
20244     
20245     
20246     
20247     // private
20248     recordToStack : function(store, prop, value, stack)
20249     {
20250         var cstore = new Roo.data.SimpleStore({
20251             //fields : this.store.reader.meta.fields, // we need array reader.. for
20252             reader : this.store.reader,
20253             data : [ ]
20254         });
20255         var _this = this;
20256         var record  = false;
20257         var srec = false;
20258         if(store.getCount() < 1){
20259             return false;
20260         }
20261         store.each(function(r){
20262             if(r.data[prop] == value){
20263                 record = r;
20264             srec = r;
20265                 return false;
20266             }
20267             if (r.data.cn && r.data.cn.length) {
20268                 cstore.loadDataFromChildren( r);
20269                 var cret = _this.recordToStack(cstore, prop, value, stack);
20270                 if (cret !== false) {
20271                     record = cret;
20272                     srec = r;
20273                     return false;
20274                 }
20275             }
20276              
20277             return true;
20278         });
20279         if (record == false) {
20280             return false
20281         }
20282         stack.unshift(srec);
20283         return record;
20284     },
20285     
20286     /*
20287      * find the stack of stores that match our value.
20288      *
20289      * 
20290      */
20291     
20292     selectActive : function ()
20293     {
20294         // if store is not loaded, then we will need to wait for that to happen first.
20295         var stack = [];
20296         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20297         for (var i = 0; i < stack.length; i++ ) {
20298             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20299         }
20300         
20301     }
20302         
20303          
20304     
20305     
20306     
20307     
20308 });/*
20309  * Based on:
20310  * Ext JS Library 1.1.1
20311  * Copyright(c) 2006-2007, Ext JS, LLC.
20312  *
20313  * Originally Released Under LGPL - original licence link has changed is not relivant.
20314  *
20315  * Fork - LGPL
20316  * <script type="text/javascript">
20317  */
20318 /**
20319  * @class Roo.form.Checkbox
20320  * @extends Roo.form.Field
20321  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20322  * @constructor
20323  * Creates a new Checkbox
20324  * @param {Object} config Configuration options
20325  */
20326 Roo.form.Checkbox = function(config){
20327     Roo.form.Checkbox.superclass.constructor.call(this, config);
20328     this.addEvents({
20329         /**
20330          * @event check
20331          * Fires when the checkbox is checked or unchecked.
20332              * @param {Roo.form.Checkbox} this This checkbox
20333              * @param {Boolean} checked The new checked value
20334              */
20335         check : true
20336     });
20337 };
20338
20339 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20340     /**
20341      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20342      */
20343     focusClass : undefined,
20344     /**
20345      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20346      */
20347     fieldClass: "x-form-field",
20348     /**
20349      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20350      */
20351     checked: false,
20352     /**
20353      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20354      * {tag: "input", type: "checkbox", autocomplete: "off"})
20355      */
20356     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20357     /**
20358      * @cfg {String} boxLabel The text that appears beside the checkbox
20359      */
20360     boxLabel : "",
20361     /**
20362      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20363      */  
20364     inputValue : '1',
20365     /**
20366      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20367      */
20368      valueOff: '0', // value when not checked..
20369
20370     actionMode : 'viewEl', 
20371     //
20372     // private
20373     itemCls : 'x-menu-check-item x-form-item',
20374     groupClass : 'x-menu-group-item',
20375     inputType : 'hidden',
20376     
20377     
20378     inSetChecked: false, // check that we are not calling self...
20379     
20380     inputElement: false, // real input element?
20381     basedOn: false, // ????
20382     
20383     isFormField: true, // not sure where this is needed!!!!
20384
20385     onResize : function(){
20386         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20387         if(!this.boxLabel){
20388             this.el.alignTo(this.wrap, 'c-c');
20389         }
20390     },
20391
20392     initEvents : function(){
20393         Roo.form.Checkbox.superclass.initEvents.call(this);
20394         this.el.on("click", this.onClick,  this);
20395         this.el.on("change", this.onClick,  this);
20396     },
20397
20398
20399     getResizeEl : function(){
20400         return this.wrap;
20401     },
20402
20403     getPositionEl : function(){
20404         return this.wrap;
20405     },
20406
20407     // private
20408     onRender : function(ct, position){
20409         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20410         /*
20411         if(this.inputValue !== undefined){
20412             this.el.dom.value = this.inputValue;
20413         }
20414         */
20415         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20416         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20417         var viewEl = this.wrap.createChild({ 
20418             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20419         this.viewEl = viewEl;   
20420         this.wrap.on('click', this.onClick,  this); 
20421         
20422         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20423         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20424         
20425         
20426         
20427         if(this.boxLabel){
20428             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20429         //    viewEl.on('click', this.onClick,  this); 
20430         }
20431         //if(this.checked){
20432             this.setChecked(this.checked);
20433         //}else{
20434             //this.checked = this.el.dom;
20435         //}
20436
20437     },
20438
20439     // private
20440     initValue : Roo.emptyFn,
20441
20442     /**
20443      * Returns the checked state of the checkbox.
20444      * @return {Boolean} True if checked, else false
20445      */
20446     getValue : function(){
20447         if(this.el){
20448             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20449         }
20450         return this.valueOff;
20451         
20452     },
20453
20454         // private
20455     onClick : function(){ 
20456         if (this.disabled) {
20457             return;
20458         }
20459         this.setChecked(!this.checked);
20460
20461         //if(this.el.dom.checked != this.checked){
20462         //    this.setValue(this.el.dom.checked);
20463        // }
20464     },
20465
20466     /**
20467      * Sets the checked state of the checkbox.
20468      * On is always based on a string comparison between inputValue and the param.
20469      * @param {Boolean/String} value - the value to set 
20470      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20471      */
20472     setValue : function(v,suppressEvent){
20473         
20474         
20475         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20476         //if(this.el && this.el.dom){
20477         //    this.el.dom.checked = this.checked;
20478         //    this.el.dom.defaultChecked = this.checked;
20479         //}
20480         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20481         //this.fireEvent("check", this, this.checked);
20482     },
20483     // private..
20484     setChecked : function(state,suppressEvent)
20485     {
20486         if (this.inSetChecked) {
20487             this.checked = state;
20488             return;
20489         }
20490         
20491     
20492         if(this.wrap){
20493             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20494         }
20495         this.checked = state;
20496         if(suppressEvent !== true){
20497             this.fireEvent('check', this, state);
20498         }
20499         this.inSetChecked = true;
20500         this.el.dom.value = state ? this.inputValue : this.valueOff;
20501         this.inSetChecked = false;
20502         
20503     },
20504     // handle setting of hidden value by some other method!!?!?
20505     setFromHidden: function()
20506     {
20507         if(!this.el){
20508             return;
20509         }
20510         //console.log("SET FROM HIDDEN");
20511         //alert('setFrom hidden');
20512         this.setValue(this.el.dom.value);
20513     },
20514     
20515     onDestroy : function()
20516     {
20517         if(this.viewEl){
20518             Roo.get(this.viewEl).remove();
20519         }
20520          
20521         Roo.form.Checkbox.superclass.onDestroy.call(this);
20522     },
20523     
20524     setBoxLabel : function(str)
20525     {
20526         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20527     }
20528
20529 });/*
20530  * Based on:
20531  * Ext JS Library 1.1.1
20532  * Copyright(c) 2006-2007, Ext JS, LLC.
20533  *
20534  * Originally Released Under LGPL - original licence link has changed is not relivant.
20535  *
20536  * Fork - LGPL
20537  * <script type="text/javascript">
20538  */
20539  
20540 /**
20541  * @class Roo.form.Radio
20542  * @extends Roo.form.Checkbox
20543  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20544  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20545  * @constructor
20546  * Creates a new Radio
20547  * @param {Object} config Configuration options
20548  */
20549 Roo.form.Radio = function(){
20550     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20551 };
20552 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20553     inputType: 'radio',
20554
20555     /**
20556      * If this radio is part of a group, it will return the selected value
20557      * @return {String}
20558      */
20559     getGroupValue : function(){
20560         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20561     },
20562     
20563     
20564     onRender : function(ct, position){
20565         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20566         
20567         if(this.inputValue !== undefined){
20568             this.el.dom.value = this.inputValue;
20569         }
20570          
20571         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20572         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20573         //var viewEl = this.wrap.createChild({ 
20574         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20575         //this.viewEl = viewEl;   
20576         //this.wrap.on('click', this.onClick,  this); 
20577         
20578         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20579         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20580         
20581         
20582         
20583         if(this.boxLabel){
20584             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20585         //    viewEl.on('click', this.onClick,  this); 
20586         }
20587          if(this.checked){
20588             this.el.dom.checked =   'checked' ;
20589         }
20590          
20591     } 
20592     
20593     
20594 });Roo.rtf = {}; // namespace
20595 Roo.rtf.Hex = function(hex)
20596 {
20597     this.hexstr = hex;
20598 };
20599 Roo.rtf.Paragraph = function(opts)
20600 {
20601     this.content = []; ///??? is that used?
20602 };Roo.rtf.Span = function(opts)
20603 {
20604     this.value = opts.value;
20605 };
20606
20607 Roo.rtf.Group = function(parent)
20608 {
20609     // we dont want to acutally store parent - it will make debug a nightmare..
20610     this.content = [];
20611     this.cn  = [];
20612      
20613        
20614     
20615 };
20616
20617 Roo.rtf.Group.prototype = {
20618     ignorable : false,
20619     content: false,
20620     cn: false,
20621     addContent : function(node) {
20622         // could set styles...
20623         this.content.push(node);
20624     },
20625     addChild : function(cn)
20626     {
20627         this.cn.push(cn);
20628     },
20629     // only for images really...
20630     toDataURL : function()
20631     {
20632         var mimetype = false;
20633         switch(true) {
20634             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20635                 mimetype = "image/png";
20636                 break;
20637              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20638                 mimetype = "image/jpeg";
20639                 break;
20640             default :
20641                 return 'about:blank'; // ?? error?
20642         }
20643         
20644         
20645         var hexstring = this.content[this.content.length-1].value;
20646         
20647         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20648             return String.fromCharCode(parseInt(a, 16));
20649         }).join(""));
20650     }
20651     
20652 };
20653 // this looks like it's normally the {rtf{ .... }}
20654 Roo.rtf.Document = function()
20655 {
20656     // we dont want to acutally store parent - it will make debug a nightmare..
20657     this.rtlch  = [];
20658     this.content = [];
20659     this.cn = [];
20660     
20661 };
20662 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20663     addChild : function(cn)
20664     {
20665         this.cn.push(cn);
20666         switch(cn.type) {
20667             case 'rtlch': // most content seems to be inside this??
20668             case 'listtext':
20669             case 'shpinst':
20670                 this.rtlch.push(cn);
20671                 return;
20672             default:
20673                 this[cn.type] = cn;
20674         }
20675         
20676     },
20677     
20678     getElementsByType : function(type)
20679     {
20680         var ret =  [];
20681         this._getElementsByType(type, ret, this.cn, 'rtf');
20682         return ret;
20683     },
20684     _getElementsByType : function (type, ret, search_array, path)
20685     {
20686         search_array.forEach(function(n,i) {
20687             if (n.type == type) {
20688                 n.path = path + '/' + n.type + ':' + i;
20689                 ret.push(n);
20690             }
20691             if (n.cn.length > 0) {
20692                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20693             }
20694         },this);
20695     }
20696     
20697 });
20698  
20699 Roo.rtf.Ctrl = function(opts)
20700 {
20701     this.value = opts.value;
20702     this.param = opts.param;
20703 };
20704 /**
20705  *
20706  *
20707  * based on this https://github.com/iarna/rtf-parser
20708  * it's really only designed to extract pict from pasted RTF 
20709  *
20710  * usage:
20711  *
20712  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20713  *  
20714  *
20715  */
20716
20717  
20718
20719
20720
20721 Roo.rtf.Parser = function(text) {
20722     //super({objectMode: true})
20723     this.text = '';
20724     this.parserState = this.parseText;
20725     
20726     // these are for interpeter...
20727     this.doc = {};
20728     ///this.parserState = this.parseTop
20729     this.groupStack = [];
20730     this.hexStore = [];
20731     this.doc = false;
20732     
20733     this.groups = []; // where we put the return.
20734     
20735     for (var ii = 0; ii < text.length; ++ii) {
20736         ++this.cpos;
20737         
20738         if (text[ii] === '\n') {
20739             ++this.row;
20740             this.col = 1;
20741         } else {
20742             ++this.col;
20743         }
20744         this.parserState(text[ii]);
20745     }
20746     
20747     
20748     
20749 };
20750 Roo.rtf.Parser.prototype = {
20751     text : '', // string being parsed..
20752     controlWord : '',
20753     controlWordParam :  '',
20754     hexChar : '',
20755     doc : false,
20756     group: false,
20757     groupStack : false,
20758     hexStore : false,
20759     
20760     
20761     cpos : 0, 
20762     row : 1, // reportin?
20763     col : 1, //
20764
20765      
20766     push : function (el)
20767     {
20768         var m = 'cmd'+ el.type;
20769         if (typeof(this[m]) == 'undefined') {
20770             Roo.log('invalid cmd:' + el.type);
20771             return;
20772         }
20773         this[m](el);
20774         //Roo.log(el);
20775     },
20776     flushHexStore : function()
20777     {
20778         if (this.hexStore.length < 1) {
20779             return;
20780         }
20781         var hexstr = this.hexStore.map(
20782             function(cmd) {
20783                 return cmd.value;
20784         }).join('');
20785         
20786         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20787               
20788             
20789         this.hexStore.splice(0)
20790         
20791     },
20792     
20793     cmdgroupstart : function()
20794     {
20795         this.flushHexStore();
20796         if (this.group) {
20797             this.groupStack.push(this.group);
20798         }
20799          // parent..
20800         if (this.doc === false) {
20801             this.group = this.doc = new Roo.rtf.Document();
20802             return;
20803             
20804         }
20805         this.group = new Roo.rtf.Group(this.group);
20806     },
20807     cmdignorable : function()
20808     {
20809         this.flushHexStore();
20810         this.group.ignorable = true;
20811     },
20812     cmdendparagraph : function()
20813     {
20814         this.flushHexStore();
20815         this.group.addContent(new Roo.rtf.Paragraph());
20816     },
20817     cmdgroupend : function ()
20818     {
20819         this.flushHexStore();
20820         var endingGroup = this.group;
20821         
20822         
20823         this.group = this.groupStack.pop();
20824         if (this.group) {
20825             this.group.addChild(endingGroup);
20826         }
20827         
20828         
20829         
20830         var doc = this.group || this.doc;
20831         //if (endingGroup instanceof FontTable) {
20832         //  doc.fonts = endingGroup.table
20833         //} else if (endingGroup instanceof ColorTable) {
20834         //  doc.colors = endingGroup.table
20835         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20836         if (endingGroup.ignorable === false) {
20837             //code
20838             this.groups.push(endingGroup);
20839            // Roo.log( endingGroup );
20840         }
20841             //Roo.each(endingGroup.content, function(item)) {
20842             //    doc.addContent(item);
20843             //}
20844             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20845         //}
20846     },
20847     cmdtext : function (cmd)
20848     {
20849         this.flushHexStore();
20850         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20851             //this.group = this.doc
20852         }
20853         this.group.addContent(new Roo.rtf.Span(cmd));
20854     },
20855     cmdcontrolword : function (cmd)
20856     {
20857         this.flushHexStore();
20858         if (!this.group.type) {
20859             this.group.type = cmd.value;
20860             return;
20861         }
20862         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20863         // we actually don't care about ctrl words...
20864         return ;
20865         /*
20866         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20867         if (this[method]) {
20868             this[method](cmd.param)
20869         } else {
20870             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20871         }
20872         */
20873     },
20874     cmdhexchar : function(cmd) {
20875         this.hexStore.push(cmd);
20876     },
20877     cmderror : function(cmd) {
20878         throw new Exception (cmd.value);
20879     },
20880     
20881     /*
20882       _flush (done) {
20883         if (this.text !== '\u0000') this.emitText()
20884         done()
20885       }
20886       */
20887       
20888       
20889     parseText : function(c)
20890     {
20891         if (c === '\\') {
20892             this.parserState = this.parseEscapes;
20893         } else if (c === '{') {
20894             this.emitStartGroup();
20895         } else if (c === '}') {
20896             this.emitEndGroup();
20897         } else if (c === '\x0A' || c === '\x0D') {
20898             // cr/lf are noise chars
20899         } else {
20900             this.text += c;
20901         }
20902     },
20903     
20904     parseEscapes: function (c)
20905     {
20906         if (c === '\\' || c === '{' || c === '}') {
20907             this.text += c;
20908             this.parserState = this.parseText;
20909         } else {
20910             this.parserState = this.parseControlSymbol;
20911             this.parseControlSymbol(c);
20912         }
20913     },
20914     parseControlSymbol: function(c)
20915     {
20916         if (c === '~') {
20917             this.text += '\u00a0'; // nbsp
20918             this.parserState = this.parseText
20919         } else if (c === '-') {
20920              this.text += '\u00ad'; // soft hyphen
20921         } else if (c === '_') {
20922             this.text += '\u2011'; // non-breaking hyphen
20923         } else if (c === '*') {
20924             this.emitIgnorable();
20925             this.parserState = this.parseText;
20926         } else if (c === "'") {
20927             this.parserState = this.parseHexChar;
20928         } else if (c === '|') { // formula cacter
20929             this.emitFormula();
20930             this.parserState = this.parseText;
20931         } else if (c === ':') { // subentry in an index entry
20932             this.emitIndexSubEntry();
20933             this.parserState = this.parseText;
20934         } else if (c === '\x0a') {
20935             this.emitEndParagraph();
20936             this.parserState = this.parseText;
20937         } else if (c === '\x0d') {
20938             this.emitEndParagraph();
20939             this.parserState = this.parseText;
20940         } else {
20941             this.parserState = this.parseControlWord;
20942             this.parseControlWord(c);
20943         }
20944     },
20945     parseHexChar: function (c)
20946     {
20947         if (/^[A-Fa-f0-9]$/.test(c)) {
20948             this.hexChar += c;
20949             if (this.hexChar.length >= 2) {
20950               this.emitHexChar();
20951               this.parserState = this.parseText;
20952             }
20953             return;
20954         }
20955         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20956         this.parserState = this.parseText;
20957         
20958     },
20959     parseControlWord : function(c)
20960     {
20961         if (c === ' ') {
20962             this.emitControlWord();
20963             this.parserState = this.parseText;
20964         } else if (/^[-\d]$/.test(c)) {
20965             this.parserState = this.parseControlWordParam;
20966             this.controlWordParam += c;
20967         } else if (/^[A-Za-z]$/.test(c)) {
20968           this.controlWord += c;
20969         } else {
20970           this.emitControlWord();
20971           this.parserState = this.parseText;
20972           this.parseText(c);
20973         }
20974     },
20975     parseControlWordParam : function (c) {
20976         if (/^\d$/.test(c)) {
20977           this.controlWordParam += c;
20978         } else if (c === ' ') {
20979           this.emitControlWord();
20980           this.parserState = this.parseText;
20981         } else {
20982           this.emitControlWord();
20983           this.parserState = this.parseText;
20984           this.parseText(c);
20985         }
20986     },
20987     
20988     
20989     
20990     
20991     emitText : function () {
20992         if (this.text === '') {
20993             return;
20994         }
20995         this.push({
20996             type: 'text',
20997             value: this.text,
20998             pos: this.cpos,
20999             row: this.row,
21000             col: this.col
21001         });
21002         this.text = ''
21003     },
21004     emitControlWord : function ()
21005     {
21006         this.emitText();
21007         if (this.controlWord === '') {
21008             this.emitError('empty control word');
21009         } else {
21010             this.push({
21011                   type: 'controlword',
21012                   value: this.controlWord,
21013                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
21014                   pos: this.cpos,
21015                   row: this.row,
21016                   col: this.col
21017             });
21018         }
21019         this.controlWord = '';
21020         this.controlWordParam = '';
21021     },
21022     emitStartGroup : function ()
21023     {
21024         this.emitText();
21025         this.push({
21026             type: 'groupstart',
21027             pos: this.cpos,
21028             row: this.row,
21029             col: this.col
21030         });
21031     },
21032     emitEndGroup : function ()
21033     {
21034         this.emitText();
21035         this.push({
21036             type: 'groupend',
21037             pos: this.cpos,
21038             row: this.row,
21039             col: this.col
21040         });
21041     },
21042     emitIgnorable : function ()
21043     {
21044         this.emitText();
21045         this.push({
21046             type: 'ignorable',
21047             pos: this.cpos,
21048             row: this.row,
21049             col: this.col
21050         });
21051     },
21052     emitHexChar : function ()
21053     {
21054         this.emitText();
21055         this.push({
21056             type: 'hexchar',
21057             value: this.hexChar,
21058             pos: this.cpos,
21059             row: this.row,
21060             col: this.col
21061         });
21062         this.hexChar = ''
21063     },
21064     emitError : function (message)
21065     {
21066       this.emitText();
21067       this.push({
21068             type: 'error',
21069             value: message,
21070             row: this.row,
21071             col: this.col,
21072             char: this.cpos //,
21073             //stack: new Error().stack
21074         });
21075     },
21076     emitEndParagraph : function () {
21077         this.emitText();
21078         this.push({
21079             type: 'endparagraph',
21080             pos: this.cpos,
21081             row: this.row,
21082             col: this.col
21083         });
21084     }
21085      
21086 } ;
21087 Roo.htmleditor = {};
21088  
21089 /**
21090  * @class Roo.htmleditor.Filter
21091  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21092  * @cfg {DomElement} node The node to iterate and filter
21093  * @cfg {boolean|String|Array} tag Tags to replace 
21094  * @constructor
21095  * Create a new Filter.
21096  * @param {Object} config Configuration options
21097  */
21098
21099
21100
21101 Roo.htmleditor.Filter = function(cfg) {
21102     Roo.apply(this.cfg);
21103     // this does not actually call walk as it's really just a abstract class
21104 }
21105
21106
21107 Roo.htmleditor.Filter.prototype = {
21108     
21109     node: false,
21110     
21111     tag: false,
21112
21113     // overrride to do replace comments.
21114     replaceComment : false,
21115     
21116     // overrride to do replace or do stuff with tags..
21117     replaceTag : false,
21118     
21119     walk : function(dom)
21120     {
21121         Roo.each( Array.from(dom.childNodes), function( e ) {
21122             switch(true) {
21123                 
21124                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
21125                     this.replaceComment(e);
21126                     return;
21127                 
21128                 case e.nodeType != 1: //not a node.
21129                     return;
21130                 
21131                 case this.tag === true: // everything
21132                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21133                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21134                     if (this.replaceTag && false === this.replaceTag(e)) {
21135                         return;
21136                     }
21137                     if (e.hasChildNodes()) {
21138                         this.walk(e);
21139                     }
21140                     return;
21141                 
21142                 default:    // tags .. that do not match.
21143                     if (e.hasChildNodes()) {
21144                         this.walk(e);
21145                     }
21146             }
21147             
21148         }, this);
21149         
21150     }
21151 }; 
21152
21153 /**
21154  * @class Roo.htmleditor.FilterAttributes
21155  * clean attributes and  styles including http:// etc.. in attribute
21156  * @constructor
21157 * Run a new Attribute Filter
21158 * @param {Object} config Configuration options
21159  */
21160 Roo.htmleditor.FilterAttributes = function(cfg)
21161 {
21162     Roo.apply(this, cfg);
21163     this.attrib_black = this.attrib_black || [];
21164     this.attrib_white = this.attrib_white || [];
21165
21166     this.attrib_clean = this.attrib_clean || [];
21167     this.style_white = this.style_white || [];
21168     this.style_black = this.style_black || [];
21169     this.walk(cfg.node);
21170 }
21171
21172 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21173 {
21174     tag: true, // all tags
21175     
21176     attrib_black : false, // array
21177     attrib_clean : false,
21178     attrib_white : false,
21179
21180     style_white : false,
21181     style_black : false,
21182      
21183      
21184     replaceTag : function(node)
21185     {
21186         if (!node.attributes || !node.attributes.length) {
21187             return true;
21188         }
21189         
21190         for (var i = node.attributes.length-1; i > -1 ; i--) {
21191             var a = node.attributes[i];
21192             //console.log(a);
21193             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21194                 node.removeAttribute(a.name);
21195                 continue;
21196             }
21197             
21198             
21199             
21200             if (a.name.toLowerCase().substr(0,2)=='on')  {
21201                 node.removeAttribute(a.name);
21202                 continue;
21203             }
21204             
21205             
21206             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21207                 node.removeAttribute(a.name);
21208                 continue;
21209             }
21210             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21211                 this.cleanAttr(node,a.name,a.value); // fixme..
21212                 continue;
21213             }
21214             if (a.name == 'style') {
21215                 this.cleanStyle(node,a.name,a.value);
21216                 continue;
21217             }
21218             /// clean up MS crap..
21219             // tecnically this should be a list of valid class'es..
21220             
21221             
21222             if (a.name == 'class') {
21223                 if (a.value.match(/^Mso/)) {
21224                     node.removeAttribute('class');
21225                 }
21226                 
21227                 if (a.value.match(/^body$/)) {
21228                     node.removeAttribute('class');
21229                 }
21230                 continue;
21231             }
21232             
21233             
21234             // style cleanup!?
21235             // class cleanup?
21236             
21237         }
21238         return true; // clean children
21239     },
21240         
21241     cleanAttr: function(node, n,v)
21242     {
21243         
21244         if (v.match(/^\./) || v.match(/^\//)) {
21245             return;
21246         }
21247         if (v.match(/^(http|https):\/\//)
21248             || v.match(/^mailto:/) 
21249             || v.match(/^ftp:/)
21250             || v.match(/^data:/)
21251             ) {
21252             return;
21253         }
21254         if (v.match(/^#/)) {
21255             return;
21256         }
21257         if (v.match(/^\{/)) { // allow template editing.
21258             return;
21259         }
21260 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21261         node.removeAttribute(n);
21262         
21263     },
21264     cleanStyle : function(node,  n,v)
21265     {
21266         if (v.match(/expression/)) { //XSS?? should we even bother..
21267             node.removeAttribute(n);
21268             return;
21269         }
21270         
21271         var parts = v.split(/;/);
21272         var clean = [];
21273         
21274         Roo.each(parts, function(p) {
21275             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21276             if (!p.length) {
21277                 return true;
21278             }
21279             var l = p.split(':').shift().replace(/\s+/g,'');
21280             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21281             
21282             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21283                 return true;
21284             }
21285             //Roo.log()
21286             // only allow 'c whitelisted system attributes'
21287             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21288                 return true;
21289             }
21290             
21291             
21292             clean.push(p);
21293             return true;
21294         },this);
21295         if (clean.length) { 
21296             node.setAttribute(n, clean.join(';'));
21297         } else {
21298             node.removeAttribute(n);
21299         }
21300         
21301     }
21302         
21303         
21304         
21305     
21306 });/**
21307  * @class Roo.htmleditor.FilterBlack
21308  * remove blacklisted elements.
21309  * @constructor
21310  * Run a new Blacklisted Filter
21311  * @param {Object} config Configuration options
21312  */
21313
21314 Roo.htmleditor.FilterBlack = function(cfg)
21315 {
21316     Roo.apply(this, cfg);
21317     this.walk(cfg.node);
21318 }
21319
21320 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21321 {
21322     tag : true, // all elements.
21323    
21324     replaceTag : function(n)
21325     {
21326         n.parentNode.removeChild(n);
21327     }
21328 });
21329 /**
21330  * @class Roo.htmleditor.FilterComment
21331  * remove comments.
21332  * @constructor
21333 * Run a new Comments Filter
21334 * @param {Object} config Configuration options
21335  */
21336 Roo.htmleditor.FilterComment = function(cfg)
21337 {
21338     this.walk(cfg.node);
21339 }
21340
21341 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21342 {
21343   
21344     replaceComment : function(n)
21345     {
21346         n.parentNode.removeChild(n);
21347     }
21348 });/**
21349  * @class Roo.htmleditor.FilterKeepChildren
21350  * remove tags but keep children
21351  * @constructor
21352  * Run a new Keep Children Filter
21353  * @param {Object} config Configuration options
21354  */
21355
21356 Roo.htmleditor.FilterKeepChildren = function(cfg)
21357 {
21358     Roo.apply(this, cfg);
21359     if (this.tag === false) {
21360         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21361     }
21362     this.walk(cfg.node);
21363 }
21364
21365 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21366 {
21367     
21368   
21369     replaceTag : function(node)
21370     {
21371         // walk children...
21372         //Roo.log(node);
21373         var ar = Array.from(node.childNodes);
21374         //remove first..
21375         for (var i = 0; i < ar.length; i++) {
21376             if (ar[i].nodeType == 1) {
21377                 if (
21378                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21379                     || // array and it matches
21380                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21381                 ) {
21382                     this.replaceTag(ar[i]); // child is blacklisted as well...
21383                     continue;
21384                 }
21385             }
21386         }  
21387         ar = Array.from(node.childNodes);
21388         for (var i = 0; i < ar.length; i++) {
21389          
21390             node.removeChild(ar[i]);
21391             // what if we need to walk these???
21392             node.parentNode.insertBefore(ar[i], node);
21393             if (this.tag !== false) {
21394                 this.walk(ar[i]);
21395                 
21396             }
21397         }
21398         node.parentNode.removeChild(node);
21399         return false; // don't walk children
21400         
21401         
21402     }
21403 });/**
21404  * @class Roo.htmleditor.FilterParagraph
21405  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21406  * like on 'push' to remove the <p> tags and replace them with line breaks.
21407  * @constructor
21408  * Run a new Paragraph Filter
21409  * @param {Object} config Configuration options
21410  */
21411
21412 Roo.htmleditor.FilterParagraph = function(cfg)
21413 {
21414     // no need to apply config.
21415     this.walk(cfg.node);
21416 }
21417
21418 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21419 {
21420     
21421      
21422     tag : 'P',
21423     
21424      
21425     replaceTag : function(node)
21426     {
21427         
21428         if (node.childNodes.length == 1 &&
21429             node.childNodes[0].nodeType == 3 &&
21430             node.childNodes[0].textContent.trim().length < 1
21431             ) {
21432             // remove and replace with '<BR>';
21433             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21434             return false; // no need to walk..
21435         }
21436         var ar = Array.from(node.childNodes);
21437         for (var i = 0; i < ar.length; i++) {
21438             node.removeChild(ar[i]);
21439             // what if we need to walk these???
21440             node.parentNode.insertBefore(ar[i], node);
21441         }
21442         // now what about this?
21443         // <p> &nbsp; </p>
21444         
21445         // double BR.
21446         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21447         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21448         node.parentNode.removeChild(node);
21449         
21450         return false;
21451
21452     }
21453     
21454 });/**
21455  * @class Roo.htmleditor.FilterSpan
21456  * filter span's with no attributes out..
21457  * @constructor
21458  * Run a new Span Filter
21459  * @param {Object} config Configuration options
21460  */
21461
21462 Roo.htmleditor.FilterSpan = function(cfg)
21463 {
21464     // no need to apply config.
21465     this.walk(cfg.node);
21466 }
21467
21468 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21469 {
21470      
21471     tag : 'SPAN',
21472      
21473  
21474     replaceTag : function(node)
21475     {
21476         if (node.attributes && node.attributes.length > 0) {
21477             return true; // walk if there are any.
21478         }
21479         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21480         return false;
21481      
21482     }
21483     
21484 });/**
21485  * @class Roo.htmleditor.FilterTableWidth
21486   try and remove table width data - as that frequently messes up other stuff.
21487  * 
21488  *      was cleanTableWidths.
21489  *
21490  * Quite often pasting from word etc.. results in tables with column and widths.
21491  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21492  *
21493  * @constructor
21494  * Run a new Table Filter
21495  * @param {Object} config Configuration options
21496  */
21497
21498 Roo.htmleditor.FilterTableWidth = function(cfg)
21499 {
21500     // no need to apply config.
21501     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21502     this.walk(cfg.node);
21503 }
21504
21505 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21506 {
21507      
21508      
21509     
21510     replaceTag: function(node) {
21511         
21512         
21513       
21514         if (node.hasAttribute('width')) {
21515             node.removeAttribute('width');
21516         }
21517         
21518          
21519         if (node.hasAttribute("style")) {
21520             // pretty basic...
21521             
21522             var styles = node.getAttribute("style").split(";");
21523             var nstyle = [];
21524             Roo.each(styles, function(s) {
21525                 if (!s.match(/:/)) {
21526                     return;
21527                 }
21528                 var kv = s.split(":");
21529                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21530                     return;
21531                 }
21532                 // what ever is left... we allow.
21533                 nstyle.push(s);
21534             });
21535             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21536             if (!nstyle.length) {
21537                 node.removeAttribute('style');
21538             }
21539         }
21540         
21541         return true; // continue doing children..
21542     }
21543 });/**
21544  * @class Roo.htmleditor.FilterWord
21545  * try and clean up all the mess that Word generates.
21546  * 
21547  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21548  
21549  * @constructor
21550  * Run a new Span Filter
21551  * @param {Object} config Configuration options
21552  */
21553
21554 Roo.htmleditor.FilterWord = function(cfg)
21555 {
21556     // no need to apply config.
21557     this.walk(cfg.node);
21558 }
21559
21560 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21561 {
21562     tag: true,
21563      
21564     
21565     /**
21566      * Clean up MS wordisms...
21567      */
21568     replaceTag : function(node)
21569     {
21570          
21571         // no idea what this does - span with text, replaceds with just text.
21572         if(
21573                 node.nodeName == 'SPAN' &&
21574                 !node.hasAttributes() &&
21575                 node.childNodes.length == 1 &&
21576                 node.firstChild.nodeName == "#text"  
21577         ) {
21578             var textNode = node.firstChild;
21579             node.removeChild(textNode);
21580             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21581                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21582             }
21583             node.parentNode.insertBefore(textNode, node);
21584             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21585                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21586             }
21587             
21588             node.parentNode.removeChild(node);
21589             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21590         }
21591         
21592    
21593         
21594         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21595             node.parentNode.removeChild(node);
21596             return false; // dont do chidlren
21597         }
21598         //Roo.log(node.tagName);
21599         // remove - but keep children..
21600         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21601             //Roo.log('-- removed');
21602             while (node.childNodes.length) {
21603                 var cn = node.childNodes[0];
21604                 node.removeChild(cn);
21605                 node.parentNode.insertBefore(cn, node);
21606                 // move node to parent - and clean it..
21607                 this.replaceTag(cn);
21608             }
21609             node.parentNode.removeChild(node);
21610             /// no need to iterate chidlren = it's got none..
21611             //this.iterateChildren(node, this.cleanWord);
21612             return false; // no need to iterate children.
21613         }
21614         // clean styles
21615         if (node.className.length) {
21616             
21617             var cn = node.className.split(/\W+/);
21618             var cna = [];
21619             Roo.each(cn, function(cls) {
21620                 if (cls.match(/Mso[a-zA-Z]+/)) {
21621                     return;
21622                 }
21623                 cna.push(cls);
21624             });
21625             node.className = cna.length ? cna.join(' ') : '';
21626             if (!cna.length) {
21627                 node.removeAttribute("class");
21628             }
21629         }
21630         
21631         if (node.hasAttribute("lang")) {
21632             node.removeAttribute("lang");
21633         }
21634         
21635         if (node.hasAttribute("style")) {
21636             
21637             var styles = node.getAttribute("style").split(";");
21638             var nstyle = [];
21639             Roo.each(styles, function(s) {
21640                 if (!s.match(/:/)) {
21641                     return;
21642                 }
21643                 var kv = s.split(":");
21644                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21645                     return;
21646                 }
21647                 // what ever is left... we allow.
21648                 nstyle.push(s);
21649             });
21650             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21651             if (!nstyle.length) {
21652                 node.removeAttribute('style');
21653             }
21654         }
21655         return true; // do children
21656         
21657         
21658         
21659     }
21660 });
21661 /**
21662  * @class Roo.htmleditor.FilterStyleToTag
21663  * part of the word stuff... - certain 'styles' should be converted to tags.
21664  * eg.
21665  *   font-weight: bold -> bold
21666  *   ?? super / subscrit etc..
21667  * 
21668  * @constructor
21669 * Run a new style to tag filter.
21670 * @param {Object} config Configuration options
21671  */
21672 Roo.htmleditor.FilterStyleToTag = function(cfg)
21673 {
21674     
21675     this.tags = {
21676         B  : [ 'fontWeight' , 'bold'],
21677         I :  [ 'fontStyle' , 'italic'],
21678         //pre :  [ 'font-style' , 'italic'],
21679         // h1.. h6 ?? font-size?
21680         SUP : [ 'verticalAlign' , 'super' ],
21681         SUB : [ 'verticalAlign' , 'sub' ]
21682         
21683         
21684     };
21685     
21686     Roo.apply(this, cfg);
21687      
21688     
21689     this.walk(cfg.node);
21690     
21691     
21692     
21693 }
21694
21695
21696 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21697 {
21698     tag: true, // all tags
21699     
21700     tags : false,
21701     
21702     
21703     replaceTag : function(node)
21704     {
21705         
21706         
21707         if (node.getAttribute("style") === null) {
21708             return true;
21709         }
21710         var inject = [];
21711         for (var k in this.tags) {
21712             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21713                 inject.push(k);
21714                 node.style.removeProperty(this.tags[k][0]);
21715             }
21716         }
21717         if (!inject.length) {
21718             return true; 
21719         }
21720         var cn = Array.from(node.childNodes);
21721         var nn = node;
21722         Roo.each(inject, function(t) {
21723             var nc = node.ownerDocument.createElement(t);
21724             nn.appendChild(nc);
21725             nn = nc;
21726         });
21727         for(var i = 0;i < cn.length;cn++) {
21728             node.removeChild(cn[i]);
21729             nn.appendChild(cn[i]);
21730         }
21731         return true /// iterate thru
21732     }
21733     
21734 })/**
21735  * @class Roo.htmleditor.FilterLongBr
21736  * BR/BR/BR - keep a maximum of 2...
21737  * @constructor
21738  * Run a new Long BR Filter
21739  * @param {Object} config Configuration options
21740  */
21741
21742 Roo.htmleditor.FilterLongBr = function(cfg)
21743 {
21744     // no need to apply config.
21745     this.walk(cfg.node);
21746 }
21747
21748 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21749 {
21750     
21751      
21752     tag : 'BR',
21753     
21754      
21755     replaceTag : function(node)
21756     {
21757         
21758         var ps = node.nextSibling;
21759         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21760             ps = ps.nextSibling;
21761         }
21762         
21763         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21764             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21765             return false;
21766         }
21767         
21768         if (!ps || ps.nodeType != 1) {
21769             return false;
21770         }
21771         
21772         if (!ps || ps.tagName != 'BR') {
21773            
21774             return false;
21775         }
21776         
21777         
21778         
21779         
21780         
21781         if (!node.previousSibling) {
21782             return false;
21783         }
21784         var ps = node.previousSibling;
21785         
21786         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21787             ps = ps.previousSibling;
21788         }
21789         if (!ps || ps.nodeType != 1) {
21790             return false;
21791         }
21792         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21793         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21794             return false;
21795         }
21796         
21797         node.parentNode.removeChild(node); // remove me...
21798         
21799         return false; // no need to do children
21800
21801     }
21802     
21803 }); 
21804
21805 /**
21806  * @class Roo.htmleditor.FilterBlock
21807  * removes id / data-block and contenteditable that are associated with blocks
21808  * usage should be done on a cloned copy of the dom
21809  * @constructor
21810 * Run a new Attribute Filter { node : xxxx }}
21811 * @param {Object} config Configuration options
21812  */
21813 Roo.htmleditor.FilterBlock = function(cfg)
21814 {
21815     Roo.apply(this, cfg);
21816     var qa = cfg.node.querySelectorAll;
21817     this.removeAttributes('data-block');
21818     this.removeAttributes('contenteditable');
21819     this.removeAttributes('id');
21820     
21821 }
21822
21823 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
21824 {
21825     node: true, // all tags
21826      
21827      
21828     removeAttributes : function(attr)
21829     {
21830         var ar = this.node.querySelectorAll('*[' + attr + ']');
21831         for (var i =0;i<ar.length;i++) {
21832             ar[i].removeAttribute(attr);
21833         }
21834     }
21835         
21836         
21837         
21838     
21839 });
21840 /***
21841  * This is based loosely on tinymce 
21842  * @class Roo.htmleditor.TidySerializer
21843  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21844  * @constructor
21845  * @method Serializer
21846  * @param {Object} settings Name/value settings object.
21847  */
21848
21849
21850 Roo.htmleditor.TidySerializer = function(settings)
21851 {
21852     Roo.apply(this, settings);
21853     
21854     this.writer = new Roo.htmleditor.TidyWriter(settings);
21855     
21856     
21857
21858 };
21859 Roo.htmleditor.TidySerializer.prototype = {
21860     
21861     /**
21862      * @param {boolean} inner do the inner of the node.
21863      */
21864     inner : false,
21865     
21866     writer : false,
21867     
21868     /**
21869     * Serializes the specified node into a string.
21870     *
21871     * @example
21872     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
21873     * @method serialize
21874     * @param {DomElement} node Node instance to serialize.
21875     * @return {String} String with HTML based on DOM tree.
21876     */
21877     serialize : function(node) {
21878         
21879         // = settings.validate;
21880         var writer = this.writer;
21881         var self  = this;
21882         this.handlers = {
21883             // #text
21884             3: function(node) {
21885                 
21886                 writer.text(node.nodeValue, node);
21887             },
21888             // #comment
21889             8: function(node) {
21890                 writer.comment(node.nodeValue);
21891             },
21892             // Processing instruction
21893             7: function(node) {
21894                 writer.pi(node.name, node.nodeValue);
21895             },
21896             // Doctype
21897             10: function(node) {
21898                 writer.doctype(node.nodeValue);
21899             },
21900             // CDATA
21901             4: function(node) {
21902                 writer.cdata(node.nodeValue);
21903             },
21904             // Document fragment
21905             11: function(node) {
21906                 node = node.firstChild;
21907                 if (!node) {
21908                     return;
21909                 }
21910                 while(node) {
21911                     self.walk(node);
21912                     node = node.nextSibling
21913                 }
21914             }
21915         };
21916         writer.reset();
21917         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
21918         return writer.getContent();
21919     },
21920
21921     walk: function(node)
21922     {
21923         var attrName, attrValue, sortedAttrs, i, l, elementRule,
21924             handler = this.handlers[node.nodeType];
21925             
21926         if (handler) {
21927             handler(node);
21928             return;
21929         }
21930     
21931         var name = node.nodeName;
21932         var isEmpty = node.childNodes.length < 1;
21933       
21934         var writer = this.writer;
21935         var attrs = node.attributes;
21936         // Sort attributes
21937         
21938         writer.start(node.nodeName, attrs, isEmpty, node);
21939         if (isEmpty) {
21940             return;
21941         }
21942         node = node.firstChild;
21943         if (!node) {
21944             writer.end(name);
21945             return;
21946         }
21947         while (node) {
21948             this.walk(node);
21949             node = node.nextSibling;
21950         }
21951         writer.end(name);
21952         
21953     
21954     }
21955     // Serialize element and treat all non elements as fragments
21956    
21957 }; 
21958
21959 /***
21960  * This is based loosely on tinymce 
21961  * @class Roo.htmleditor.TidyWriter
21962  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21963  *
21964  * Known issues?
21965  * - not tested much with 'PRE' formated elements.
21966  * 
21967  *
21968  *
21969  */
21970
21971 Roo.htmleditor.TidyWriter = function(settings)
21972 {
21973     
21974     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
21975     Roo.apply(this, settings);
21976     this.html = [];
21977     this.state = [];
21978      
21979     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
21980   
21981 }
21982 Roo.htmleditor.TidyWriter.prototype = {
21983
21984  
21985     state : false,
21986     
21987     indent :  '  ',
21988     
21989     // part of state...
21990     indentstr : '',
21991     in_pre: false,
21992     in_inline : false,
21993     last_inline : false,
21994     encode : false,
21995      
21996     
21997             /**
21998     * Writes the a start element such as <p id="a">.
21999     *
22000     * @method start
22001     * @param {String} name Name of the element.
22002     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22003     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22004     */
22005     start: function(name, attrs, empty, node)
22006     {
22007         var i, l, attr, value;
22008         
22009         // there are some situations where adding line break && indentation will not work. will not work.
22010         // <span / b / i ... formating?
22011         
22012         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22013         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22014         
22015         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22016         
22017         var add_lb = name == 'BR' ? false : in_inline;
22018         
22019         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22020             i_inline = false;
22021         }
22022
22023         var indentstr =  this.indentstr;
22024         
22025         // e_inline = elements that can be inline, but still allow \n before and after?
22026         // only 'BR' ??? any others?
22027         
22028         // ADD LINE BEFORE tage
22029         if (!this.in_pre) {
22030             if (in_inline) {
22031                 //code
22032                 if (name == 'BR') {
22033                     this.addLine();
22034                 } else if (this.lastElementEndsWS()) {
22035                     this.addLine();
22036                 } else{
22037                     // otherwise - no new line. (and dont indent.)
22038                     indentstr = '';
22039                 }
22040                 
22041             } else {
22042                 this.addLine();
22043             }
22044         } else {
22045             indentstr = '';
22046         }
22047         
22048         this.html.push(indentstr + '<', name.toLowerCase());
22049         
22050         if (attrs) {
22051             for (i = 0, l = attrs.length; i < l; i++) {
22052                 attr = attrs[i];
22053                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22054             }
22055         }
22056      
22057         if (empty) {
22058             if (is_short) {
22059                 this.html[this.html.length] = '/>';
22060             } else {
22061                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22062             }
22063             var e_inline = name == 'BR' ? false : this.in_inline;
22064             
22065             if (!e_inline && !this.in_pre) {
22066                 this.addLine();
22067             }
22068             return;
22069         
22070         }
22071         // not empty..
22072         this.html[this.html.length] = '>';
22073         
22074         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22075         /*
22076         if (!in_inline && !in_pre) {
22077             var cn = node.firstChild;
22078             while(cn) {
22079                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22080                     in_inline = true
22081                     break;
22082                 }
22083                 cn = cn.nextSibling;
22084             }
22085              
22086         }
22087         */
22088         
22089         
22090         this.pushState({
22091             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
22092             in_pre : in_pre,
22093             in_inline :  in_inline
22094         });
22095         // add a line after if we are not in a
22096         
22097         if (!in_inline && !in_pre) {
22098             this.addLine();
22099         }
22100         
22101             
22102          
22103         
22104     },
22105     
22106     lastElementEndsWS : function()
22107     {
22108         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22109         if (value === false) {
22110             return true;
22111         }
22112         return value.match(/\s+$/);
22113         
22114     },
22115     
22116     /**
22117      * Writes the a end element such as </p>.
22118      *
22119      * @method end
22120      * @param {String} name Name of the element.
22121      */
22122     end: function(name) {
22123         var value;
22124         this.popState();
22125         var indentstr = '';
22126         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22127         
22128         if (!this.in_pre && !in_inline) {
22129             this.addLine();
22130             indentstr  = this.indentstr;
22131         }
22132         this.html.push(indentstr + '</', name.toLowerCase(), '>');
22133         this.last_inline = in_inline;
22134         
22135         // pop the indent state..
22136     },
22137     /**
22138      * Writes a text node.
22139      *
22140      * In pre - we should not mess with the contents.
22141      * 
22142      *
22143      * @method text
22144      * @param {String} text String to write out.
22145      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22146      */
22147     text: function(text, node)
22148     {
22149         // if not in whitespace critical
22150         if (text.length < 1) {
22151             return;
22152         }
22153         if (this.in_pre) {
22154             this.html[this.html.length] =  text;
22155             return;   
22156         }
22157         
22158         if (this.in_inline) {
22159             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22160             if (text != ' ') {
22161                 text = text.replace(/\s+/,' ');  // all white space to single white space
22162                 
22163                     
22164                 // if next tag is '<BR>', then we can trim right..
22165                 if (node.nextSibling &&
22166                     node.nextSibling.nodeType == 1 &&
22167                     node.nextSibling.nodeName == 'BR' )
22168                 {
22169                     text = text.replace(/\s+$/g,'');
22170                 }
22171                 // if previous tag was a BR, we can also trim..
22172                 if (node.previousSibling &&
22173                     node.previousSibling.nodeType == 1 &&
22174                     node.previousSibling.nodeName == 'BR' )
22175                 {
22176                     text = this.indentstr +  text.replace(/^\s+/g,'');
22177                 }
22178                 if (text.match(/\n/)) {
22179                     text = text.replace(
22180                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22181                     );
22182                     // remoeve the last whitespace / line break.
22183                     text = text.replace(/\n\s+$/,'');
22184                 }
22185                 // repace long lines
22186                 
22187             }
22188              
22189             this.html[this.html.length] =  text;
22190             return;   
22191         }
22192         // see if previous element was a inline element.
22193         var indentstr = this.indentstr;
22194    
22195         text = text.replace(/\s+/g," "); // all whitespace into single white space.
22196         
22197         // should trim left?
22198         if (node.previousSibling &&
22199             node.previousSibling.nodeType == 1 &&
22200             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22201         {
22202             indentstr = '';
22203             
22204         } else {
22205             this.addLine();
22206             text = text.replace(/^\s+/,''); // trim left
22207           
22208         }
22209         // should trim right?
22210         if (node.nextSibling &&
22211             node.nextSibling.nodeType == 1 &&
22212             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22213         {
22214           // noop
22215             
22216         }  else {
22217             text = text.replace(/\s+$/,''); // trim right
22218         }
22219          
22220               
22221         
22222         
22223         
22224         if (text.length < 1) {
22225             return;
22226         }
22227         if (!text.match(/\n/)) {
22228             this.html.push(indentstr + text);
22229             return;
22230         }
22231         
22232         text = this.indentstr + text.replace(
22233             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22234         );
22235         // remoeve the last whitespace / line break.
22236         text = text.replace(/\s+$/,''); 
22237         
22238         this.html.push(text);
22239         
22240         // split and indent..
22241         
22242         
22243     },
22244     /**
22245      * Writes a cdata node such as <![CDATA[data]]>.
22246      *
22247      * @method cdata
22248      * @param {String} text String to write out inside the cdata.
22249      */
22250     cdata: function(text) {
22251         this.html.push('<![CDATA[', text, ']]>');
22252     },
22253     /**
22254     * Writes a comment node such as <!-- Comment -->.
22255     *
22256     * @method cdata
22257     * @param {String} text String to write out inside the comment.
22258     */
22259    comment: function(text) {
22260        this.html.push('<!--', text, '-->');
22261    },
22262     /**
22263      * Writes a PI node such as <?xml attr="value" ?>.
22264      *
22265      * @method pi
22266      * @param {String} name Name of the pi.
22267      * @param {String} text String to write out inside the pi.
22268      */
22269     pi: function(name, text) {
22270         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22271         this.indent != '' && this.html.push('\n');
22272     },
22273     /**
22274      * Writes a doctype node such as <!DOCTYPE data>.
22275      *
22276      * @method doctype
22277      * @param {String} text String to write out inside the doctype.
22278      */
22279     doctype: function(text) {
22280         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22281     },
22282     /**
22283      * Resets the internal buffer if one wants to reuse the writer.
22284      *
22285      * @method reset
22286      */
22287     reset: function() {
22288         this.html.length = 0;
22289         this.state = [];
22290         this.pushState({
22291             indentstr : '',
22292             in_pre : false, 
22293             in_inline : false
22294         })
22295     },
22296     /**
22297      * Returns the contents that got serialized.
22298      *
22299      * @method getContent
22300      * @return {String} HTML contents that got written down.
22301      */
22302     getContent: function() {
22303         return this.html.join('').replace(/\n$/, '');
22304     },
22305     
22306     pushState : function(cfg)
22307     {
22308         this.state.push(cfg);
22309         Roo.apply(this, cfg);
22310     },
22311     
22312     popState : function()
22313     {
22314         if (this.state.length < 1) {
22315             return; // nothing to push
22316         }
22317         var cfg = {
22318             in_pre: false,
22319             indentstr : ''
22320         };
22321         this.state.pop();
22322         if (this.state.length > 0) {
22323             cfg = this.state[this.state.length-1]; 
22324         }
22325         Roo.apply(this, cfg);
22326     },
22327     
22328     addLine: function()
22329     {
22330         if (this.html.length < 1) {
22331             return;
22332         }
22333         
22334         
22335         var value = this.html[this.html.length - 1];
22336         if (value.length > 0 && '\n' !== value) {
22337             this.html.push('\n');
22338         }
22339     }
22340     
22341     
22342 //'pre script noscript style textarea video audio iframe object code'
22343 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
22344 // inline 
22345 };
22346
22347 Roo.htmleditor.TidyWriter.inline_elements = [
22348         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22349         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP'
22350 ];
22351 Roo.htmleditor.TidyWriter.shortend_elements = [
22352     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22353     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22354 ];
22355
22356 Roo.htmleditor.TidyWriter.whitespace_elements = [
22357     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22358 ];/***
22359  * This is based loosely on tinymce 
22360  * @class Roo.htmleditor.TidyEntities
22361  * @static
22362  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22363  *
22364  * Not 100% sure this is actually used or needed.
22365  */
22366
22367 Roo.htmleditor.TidyEntities = {
22368     
22369     /**
22370      * initialize data..
22371      */
22372     init : function (){
22373      
22374         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22375        
22376     },
22377
22378
22379     buildEntitiesLookup: function(items, radix) {
22380         var i, chr, entity, lookup = {};
22381         if (!items) {
22382             return {};
22383         }
22384         items = typeof(items) == 'string' ? items.split(',') : items;
22385         radix = radix || 10;
22386         // Build entities lookup table
22387         for (i = 0; i < items.length; i += 2) {
22388             chr = String.fromCharCode(parseInt(items[i], radix));
22389             // Only add non base entities
22390             if (!this.baseEntities[chr]) {
22391                 entity = '&' + items[i + 1] + ';';
22392                 lookup[chr] = entity;
22393                 lookup[entity] = chr;
22394             }
22395         }
22396         return lookup;
22397         
22398     },
22399     
22400     asciiMap : {
22401             128: '€',
22402             130: '‚',
22403             131: 'ƒ',
22404             132: '„',
22405             133: '…',
22406             134: '†',
22407             135: '‡',
22408             136: 'ˆ',
22409             137: '‰',
22410             138: 'Š',
22411             139: '‹',
22412             140: 'Œ',
22413             142: 'Ž',
22414             145: '‘',
22415             146: '’',
22416             147: '“',
22417             148: '”',
22418             149: '•',
22419             150: '–',
22420             151: '—',
22421             152: '˜',
22422             153: '™',
22423             154: 'š',
22424             155: '›',
22425             156: 'œ',
22426             158: 'ž',
22427             159: 'Ÿ'
22428     },
22429     // Raw entities
22430     baseEntities : {
22431         '"': '&quot;',
22432         // Needs to be escaped since the YUI compressor would otherwise break the code
22433         '\'': '&#39;',
22434         '<': '&lt;',
22435         '>': '&gt;',
22436         '&': '&amp;',
22437         '`': '&#96;'
22438     },
22439     // Reverse lookup table for raw entities
22440     reverseEntities : {
22441         '&lt;': '<',
22442         '&gt;': '>',
22443         '&amp;': '&',
22444         '&quot;': '"',
22445         '&apos;': '\''
22446     },
22447     
22448     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22449     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22450     rawCharsRegExp : /[<>&\"\']/g,
22451     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22452     namedEntities  : false,
22453     namedEntitiesData : [ 
22454         '50',
22455         'nbsp',
22456         '51',
22457         'iexcl',
22458         '52',
22459         'cent',
22460         '53',
22461         'pound',
22462         '54',
22463         'curren',
22464         '55',
22465         'yen',
22466         '56',
22467         'brvbar',
22468         '57',
22469         'sect',
22470         '58',
22471         'uml',
22472         '59',
22473         'copy',
22474         '5a',
22475         'ordf',
22476         '5b',
22477         'laquo',
22478         '5c',
22479         'not',
22480         '5d',
22481         'shy',
22482         '5e',
22483         'reg',
22484         '5f',
22485         'macr',
22486         '5g',
22487         'deg',
22488         '5h',
22489         'plusmn',
22490         '5i',
22491         'sup2',
22492         '5j',
22493         'sup3',
22494         '5k',
22495         'acute',
22496         '5l',
22497         'micro',
22498         '5m',
22499         'para',
22500         '5n',
22501         'middot',
22502         '5o',
22503         'cedil',
22504         '5p',
22505         'sup1',
22506         '5q',
22507         'ordm',
22508         '5r',
22509         'raquo',
22510         '5s',
22511         'frac14',
22512         '5t',
22513         'frac12',
22514         '5u',
22515         'frac34',
22516         '5v',
22517         'iquest',
22518         '60',
22519         'Agrave',
22520         '61',
22521         'Aacute',
22522         '62',
22523         'Acirc',
22524         '63',
22525         'Atilde',
22526         '64',
22527         'Auml',
22528         '65',
22529         'Aring',
22530         '66',
22531         'AElig',
22532         '67',
22533         'Ccedil',
22534         '68',
22535         'Egrave',
22536         '69',
22537         'Eacute',
22538         '6a',
22539         'Ecirc',
22540         '6b',
22541         'Euml',
22542         '6c',
22543         'Igrave',
22544         '6d',
22545         'Iacute',
22546         '6e',
22547         'Icirc',
22548         '6f',
22549         'Iuml',
22550         '6g',
22551         'ETH',
22552         '6h',
22553         'Ntilde',
22554         '6i',
22555         'Ograve',
22556         '6j',
22557         'Oacute',
22558         '6k',
22559         'Ocirc',
22560         '6l',
22561         'Otilde',
22562         '6m',
22563         'Ouml',
22564         '6n',
22565         'times',
22566         '6o',
22567         'Oslash',
22568         '6p',
22569         'Ugrave',
22570         '6q',
22571         'Uacute',
22572         '6r',
22573         'Ucirc',
22574         '6s',
22575         'Uuml',
22576         '6t',
22577         'Yacute',
22578         '6u',
22579         'THORN',
22580         '6v',
22581         'szlig',
22582         '70',
22583         'agrave',
22584         '71',
22585         'aacute',
22586         '72',
22587         'acirc',
22588         '73',
22589         'atilde',
22590         '74',
22591         'auml',
22592         '75',
22593         'aring',
22594         '76',
22595         'aelig',
22596         '77',
22597         'ccedil',
22598         '78',
22599         'egrave',
22600         '79',
22601         'eacute',
22602         '7a',
22603         'ecirc',
22604         '7b',
22605         'euml',
22606         '7c',
22607         'igrave',
22608         '7d',
22609         'iacute',
22610         '7e',
22611         'icirc',
22612         '7f',
22613         'iuml',
22614         '7g',
22615         'eth',
22616         '7h',
22617         'ntilde',
22618         '7i',
22619         'ograve',
22620         '7j',
22621         'oacute',
22622         '7k',
22623         'ocirc',
22624         '7l',
22625         'otilde',
22626         '7m',
22627         'ouml',
22628         '7n',
22629         'divide',
22630         '7o',
22631         'oslash',
22632         '7p',
22633         'ugrave',
22634         '7q',
22635         'uacute',
22636         '7r',
22637         'ucirc',
22638         '7s',
22639         'uuml',
22640         '7t',
22641         'yacute',
22642         '7u',
22643         'thorn',
22644         '7v',
22645         'yuml',
22646         'ci',
22647         'fnof',
22648         'sh',
22649         'Alpha',
22650         'si',
22651         'Beta',
22652         'sj',
22653         'Gamma',
22654         'sk',
22655         'Delta',
22656         'sl',
22657         'Epsilon',
22658         'sm',
22659         'Zeta',
22660         'sn',
22661         'Eta',
22662         'so',
22663         'Theta',
22664         'sp',
22665         'Iota',
22666         'sq',
22667         'Kappa',
22668         'sr',
22669         'Lambda',
22670         'ss',
22671         'Mu',
22672         'st',
22673         'Nu',
22674         'su',
22675         'Xi',
22676         'sv',
22677         'Omicron',
22678         't0',
22679         'Pi',
22680         't1',
22681         'Rho',
22682         't3',
22683         'Sigma',
22684         't4',
22685         'Tau',
22686         't5',
22687         'Upsilon',
22688         't6',
22689         'Phi',
22690         't7',
22691         'Chi',
22692         't8',
22693         'Psi',
22694         't9',
22695         'Omega',
22696         'th',
22697         'alpha',
22698         'ti',
22699         'beta',
22700         'tj',
22701         'gamma',
22702         'tk',
22703         'delta',
22704         'tl',
22705         'epsilon',
22706         'tm',
22707         'zeta',
22708         'tn',
22709         'eta',
22710         'to',
22711         'theta',
22712         'tp',
22713         'iota',
22714         'tq',
22715         'kappa',
22716         'tr',
22717         'lambda',
22718         'ts',
22719         'mu',
22720         'tt',
22721         'nu',
22722         'tu',
22723         'xi',
22724         'tv',
22725         'omicron',
22726         'u0',
22727         'pi',
22728         'u1',
22729         'rho',
22730         'u2',
22731         'sigmaf',
22732         'u3',
22733         'sigma',
22734         'u4',
22735         'tau',
22736         'u5',
22737         'upsilon',
22738         'u6',
22739         'phi',
22740         'u7',
22741         'chi',
22742         'u8',
22743         'psi',
22744         'u9',
22745         'omega',
22746         'uh',
22747         'thetasym',
22748         'ui',
22749         'upsih',
22750         'um',
22751         'piv',
22752         '812',
22753         'bull',
22754         '816',
22755         'hellip',
22756         '81i',
22757         'prime',
22758         '81j',
22759         'Prime',
22760         '81u',
22761         'oline',
22762         '824',
22763         'frasl',
22764         '88o',
22765         'weierp',
22766         '88h',
22767         'image',
22768         '88s',
22769         'real',
22770         '892',
22771         'trade',
22772         '89l',
22773         'alefsym',
22774         '8cg',
22775         'larr',
22776         '8ch',
22777         'uarr',
22778         '8ci',
22779         'rarr',
22780         '8cj',
22781         'darr',
22782         '8ck',
22783         'harr',
22784         '8dl',
22785         'crarr',
22786         '8eg',
22787         'lArr',
22788         '8eh',
22789         'uArr',
22790         '8ei',
22791         'rArr',
22792         '8ej',
22793         'dArr',
22794         '8ek',
22795         'hArr',
22796         '8g0',
22797         'forall',
22798         '8g2',
22799         'part',
22800         '8g3',
22801         'exist',
22802         '8g5',
22803         'empty',
22804         '8g7',
22805         'nabla',
22806         '8g8',
22807         'isin',
22808         '8g9',
22809         'notin',
22810         '8gb',
22811         'ni',
22812         '8gf',
22813         'prod',
22814         '8gh',
22815         'sum',
22816         '8gi',
22817         'minus',
22818         '8gn',
22819         'lowast',
22820         '8gq',
22821         'radic',
22822         '8gt',
22823         'prop',
22824         '8gu',
22825         'infin',
22826         '8h0',
22827         'ang',
22828         '8h7',
22829         'and',
22830         '8h8',
22831         'or',
22832         '8h9',
22833         'cap',
22834         '8ha',
22835         'cup',
22836         '8hb',
22837         'int',
22838         '8hk',
22839         'there4',
22840         '8hs',
22841         'sim',
22842         '8i5',
22843         'cong',
22844         '8i8',
22845         'asymp',
22846         '8j0',
22847         'ne',
22848         '8j1',
22849         'equiv',
22850         '8j4',
22851         'le',
22852         '8j5',
22853         'ge',
22854         '8k2',
22855         'sub',
22856         '8k3',
22857         'sup',
22858         '8k4',
22859         'nsub',
22860         '8k6',
22861         'sube',
22862         '8k7',
22863         'supe',
22864         '8kl',
22865         'oplus',
22866         '8kn',
22867         'otimes',
22868         '8l5',
22869         'perp',
22870         '8m5',
22871         'sdot',
22872         '8o8',
22873         'lceil',
22874         '8o9',
22875         'rceil',
22876         '8oa',
22877         'lfloor',
22878         '8ob',
22879         'rfloor',
22880         '8p9',
22881         'lang',
22882         '8pa',
22883         'rang',
22884         '9ea',
22885         'loz',
22886         '9j0',
22887         'spades',
22888         '9j3',
22889         'clubs',
22890         '9j5',
22891         'hearts',
22892         '9j6',
22893         'diams',
22894         'ai',
22895         'OElig',
22896         'aj',
22897         'oelig',
22898         'b0',
22899         'Scaron',
22900         'b1',
22901         'scaron',
22902         'bo',
22903         'Yuml',
22904         'm6',
22905         'circ',
22906         'ms',
22907         'tilde',
22908         '802',
22909         'ensp',
22910         '803',
22911         'emsp',
22912         '809',
22913         'thinsp',
22914         '80c',
22915         'zwnj',
22916         '80d',
22917         'zwj',
22918         '80e',
22919         'lrm',
22920         '80f',
22921         'rlm',
22922         '80j',
22923         'ndash',
22924         '80k',
22925         'mdash',
22926         '80o',
22927         'lsquo',
22928         '80p',
22929         'rsquo',
22930         '80q',
22931         'sbquo',
22932         '80s',
22933         'ldquo',
22934         '80t',
22935         'rdquo',
22936         '80u',
22937         'bdquo',
22938         '810',
22939         'dagger',
22940         '811',
22941         'Dagger',
22942         '81g',
22943         'permil',
22944         '81p',
22945         'lsaquo',
22946         '81q',
22947         'rsaquo',
22948         '85c',
22949         'euro'
22950     ],
22951
22952          
22953     /**
22954      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
22955      *
22956      * @method encodeRaw
22957      * @param {String} text Text to encode.
22958      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22959      * @return {String} Entity encoded text.
22960      */
22961     encodeRaw: function(text, attr)
22962     {
22963         var t = this;
22964         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
22965             return t.baseEntities[chr] || chr;
22966         });
22967     },
22968     /**
22969      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
22970      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
22971      * and is exposed as the DOMUtils.encode function.
22972      *
22973      * @method encodeAllRaw
22974      * @param {String} text Text to encode.
22975      * @return {String} Entity encoded text.
22976      */
22977     encodeAllRaw: function(text) {
22978         var t = this;
22979         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
22980             return t.baseEntities[chr] || chr;
22981         });
22982     },
22983     /**
22984      * Encodes the specified string using numeric entities. The core entities will be
22985      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
22986      *
22987      * @method encodeNumeric
22988      * @param {String} text Text to encode.
22989      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22990      * @return {String} Entity encoded text.
22991      */
22992     encodeNumeric: function(text, attr) {
22993         var t = this;
22994         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
22995             // Multi byte sequence convert it to a single entity
22996             if (chr.length > 1) {
22997                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
22998             }
22999             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23000         });
23001     },
23002     /**
23003      * Encodes the specified string using named entities. The core entities will be encoded
23004      * as named ones but all non lower ascii characters will be encoded into named entities.
23005      *
23006      * @method encodeNamed
23007      * @param {String} text Text to encode.
23008      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23009      * @param {Object} entities Optional parameter with entities to use.
23010      * @return {String} Entity encoded text.
23011      */
23012     encodeNamed: function(text, attr, entities) {
23013         var t = this;
23014         entities = entities || this.namedEntities;
23015         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23016             return t.baseEntities[chr] || entities[chr] || chr;
23017         });
23018     },
23019     /**
23020      * Returns an encode function based on the name(s) and it's optional entities.
23021      *
23022      * @method getEncodeFunc
23023      * @param {String} name Comma separated list of encoders for example named,numeric.
23024      * @param {String} entities Optional parameter with entities to use instead of the built in set.
23025      * @return {function} Encode function to be used.
23026      */
23027     getEncodeFunc: function(name, entities) {
23028         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23029         var t = this;
23030         function encodeNamedAndNumeric(text, attr) {
23031             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23032                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23033             });
23034         }
23035
23036         function encodeCustomNamed(text, attr) {
23037             return t.encodeNamed(text, attr, entities);
23038         }
23039         // Replace + with , to be compatible with previous TinyMCE versions
23040         name = this.makeMap(name.replace(/\+/g, ','));
23041         // Named and numeric encoder
23042         if (name.named && name.numeric) {
23043             return this.encodeNamedAndNumeric;
23044         }
23045         // Named encoder
23046         if (name.named) {
23047             // Custom names
23048             if (entities) {
23049                 return encodeCustomNamed;
23050             }
23051             return this.encodeNamed;
23052         }
23053         // Numeric
23054         if (name.numeric) {
23055             return this.encodeNumeric;
23056         }
23057         // Raw encoder
23058         return this.encodeRaw;
23059     },
23060     /**
23061      * Decodes the specified string, this will replace entities with raw UTF characters.
23062      *
23063      * @method decode
23064      * @param {String} text Text to entity decode.
23065      * @return {String} Entity decoded string.
23066      */
23067     decode: function(text)
23068     {
23069         var  t = this;
23070         return text.replace(this.entityRegExp, function(all, numeric) {
23071             if (numeric) {
23072                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23073                 // Support upper UTF
23074                 if (numeric > 65535) {
23075                     numeric -= 65536;
23076                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23077                 }
23078                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23079             }
23080             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23081         });
23082     },
23083     nativeDecode : function (text) {
23084         return text;
23085     },
23086     makeMap : function (items, delim, map) {
23087                 var i;
23088                 items = items || [];
23089                 delim = delim || ',';
23090                 if (typeof items == "string") {
23091                         items = items.split(delim);
23092                 }
23093                 map = map || {};
23094                 i = items.length;
23095                 while (i--) {
23096                         map[items[i]] = {};
23097                 }
23098                 return map;
23099         }
23100 };
23101     
23102     
23103     
23104 Roo.htmleditor.TidyEntities.init();
23105 /**
23106  * @class Roo.htmleditor.KeyEnter
23107  * Handle Enter press..
23108  * @cfg {Roo.HtmlEditorCore} core the editor.
23109  * @constructor
23110  * Create a new Filter.
23111  * @param {Object} config Configuration options
23112  */
23113
23114
23115
23116
23117
23118 Roo.htmleditor.KeyEnter = function(cfg) {
23119     Roo.apply(this, cfg);
23120     // this does not actually call walk as it's really just a abstract class
23121  
23122     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23123 }
23124
23125 //Roo.htmleditor.KeyEnter.i = 0;
23126
23127
23128 Roo.htmleditor.KeyEnter.prototype = {
23129     
23130     core : false,
23131     
23132     keypress : function(e)
23133     {
23134         if (e.charCode != 13 && e.charCode != 10) {
23135             Roo.log([e.charCode,e]);
23136             return true;
23137         }
23138         e.preventDefault();
23139         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23140         var doc = this.core.doc;
23141           //add a new line
23142        
23143     
23144         var sel = this.core.getSelection();
23145         var range = sel.getRangeAt(0);
23146         var n = range.commonAncestorContainer;
23147         var pc = range.closest([ 'ol', 'ul']);
23148         var pli = range.closest('li');
23149         if (!pc || e.ctrlKey) {
23150             sel.insertNode('br', 'after'); 
23151          
23152             this.core.undoManager.addEvent();
23153             this.core.fireEditorEvent(e);
23154             return false;
23155         }
23156         
23157         // deal with <li> insetion
23158         if (pli.innerText.trim() == '' &&
23159             pli.previousSibling &&
23160             pli.previousSibling.nodeName == 'LI' &&
23161             pli.previousSibling.innerText.trim() ==  '') {
23162             pli.parentNode.removeChild(pli.previousSibling);
23163             sel.cursorAfter(pc);
23164             this.core.undoManager.addEvent();
23165             this.core.fireEditorEvent(e);
23166             return false;
23167         }
23168     
23169         var li = doc.createElement('LI');
23170         li.innerHTML = '&nbsp;';
23171         if (!pli || !pli.firstSibling) {
23172             pc.appendChild(li);
23173         } else {
23174             pli.parentNode.insertBefore(li, pli.firstSibling);
23175         }
23176         sel.cursorText (li.firstChild);
23177       
23178         this.core.undoManager.addEvent();
23179         this.core.fireEditorEvent(e);
23180
23181         return false;
23182         
23183     
23184         
23185         
23186          
23187     }
23188 };
23189      
23190 /**
23191  * @class Roo.htmleditor.Block
23192  * Base class for html editor blocks - do not use it directly .. extend it..
23193  * @cfg {DomElement} node The node to apply stuff to.
23194  * @cfg {String} friendly_name the name that appears in the context bar about this block
23195  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23196  
23197  * @constructor
23198  * Create a new Filter.
23199  * @param {Object} config Configuration options
23200  */
23201
23202 Roo.htmleditor.Block  = function(cfg)
23203 {
23204     // do nothing .. should not be called really.
23205 }
23206 /**
23207  * factory method to get the block from an element (using cache if necessary)
23208  * @static
23209  * @param {HtmlElement} the dom element
23210  */
23211 Roo.htmleditor.Block.factory = function(node)
23212 {
23213     var cc = Roo.htmleditor.Block.cache;
23214     var id = Roo.get(node).id;
23215     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23216         Roo.htmleditor.Block.cache[id].readElement(node);
23217         return Roo.htmleditor.Block.cache[id];
23218     }
23219     var db  = node.getAttribute('data-block');
23220     if (!db) {
23221         db = node.nodeName.toLowerCase().toUpperCaseFirst();
23222     }
23223     var cls = Roo.htmleditor['Block' + db];
23224     if (typeof(cls) == 'undefined') {
23225         //Roo.log(node.getAttribute('data-block'));
23226         Roo.log("OOps missing block : " + 'Block' + db);
23227         return false;
23228     }
23229     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23230     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
23231 };
23232
23233 /**
23234  * initalize all Elements from content that are 'blockable'
23235  * @static
23236  * @param the body element
23237  */
23238 Roo.htmleditor.Block.initAll = function(body, type)
23239 {
23240     if (typeof(type) == 'undefined') {
23241         var ia = Roo.htmleditor.Block.initAll;
23242         ia(body,'table');
23243         ia(body,'td');
23244         ia(body,'figure');
23245         return;
23246     }
23247     Roo.each(Roo.get(body).query(type), function(e) {
23248         Roo.htmleditor.Block.factory(e);    
23249     },this);
23250 };
23251 // question goes here... do we need to clear out this cache sometimes?
23252 // or show we make it relivant to the htmleditor.
23253 Roo.htmleditor.Block.cache = {};
23254
23255 Roo.htmleditor.Block.prototype = {
23256     
23257     node : false,
23258     
23259      // used by context menu
23260     friendly_name : 'Based Block',
23261     
23262     // text for button to delete this element
23263     deleteTitle : false,
23264     
23265     context : false,
23266     /**
23267      * Update a node with values from this object
23268      * @param {DomElement} node
23269      */
23270     updateElement : function(node)
23271     {
23272         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23273     },
23274      /**
23275      * convert to plain HTML for calling insertAtCursor..
23276      */
23277     toHTML : function()
23278     {
23279         return Roo.DomHelper.markup(this.toObject());
23280     },
23281     /**
23282      * used by readEleemnt to extract data from a node
23283      * may need improving as it's pretty basic
23284      
23285      * @param {DomElement} node
23286      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23287      * @param {String} attribute (use html - for contents, or style for using next param as style)
23288      * @param {String} style the style property - eg. text-align
23289      */
23290     getVal : function(node, tag, attr, style)
23291     {
23292         var n = node;
23293         if (tag !== true && n.tagName != tag.toUpperCase()) {
23294             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23295             // but kiss for now.
23296             n = node.getElementsByTagName(tag).item(0);
23297         }
23298         if (!n) {
23299             return '';
23300         }
23301         if (attr == 'html') {
23302             return n.innerHTML;
23303         }
23304         if (attr == 'style') {
23305             return n.style[style]; 
23306         }
23307         
23308         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23309             
23310     },
23311     /**
23312      * create a DomHelper friendly object - for use with 
23313      * Roo.DomHelper.markup / overwrite / etc..
23314      * (override this)
23315      */
23316     toObject : function()
23317     {
23318         return {};
23319     },
23320       /**
23321      * Read a node that has a 'data-block' property - and extract the values from it.
23322      * @param {DomElement} node - the node
23323      */
23324     readElement : function(node)
23325     {
23326         
23327     } 
23328     
23329     
23330 };
23331
23332  
23333
23334 /**
23335  * @class Roo.htmleditor.BlockFigure
23336  * Block that has an image and a figcaption
23337  * @cfg {String} image_src the url for the image
23338  * @cfg {String} align (left|right) alignment for the block default left
23339  * @cfg {String} caption the text to appear below  (and in the alt tag)
23340  * @cfg {String} caption_display (block|none) display or not the caption
23341  * @cfg {String|number} image_width the width of the image number or %?
23342  * @cfg {String|number} image_height the height of the image number or %?
23343  * 
23344  * @constructor
23345  * Create a new Filter.
23346  * @param {Object} config Configuration options
23347  */
23348
23349 Roo.htmleditor.BlockFigure = function(cfg)
23350 {
23351     if (cfg.node) {
23352         this.readElement(cfg.node);
23353         this.updateElement(cfg.node);
23354     }
23355     Roo.apply(this, cfg);
23356 }
23357 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23358  
23359     
23360     // setable values.
23361     image_src: '',
23362     align: 'center',
23363     caption : '',
23364     caption_display : 'block',
23365     width : '100%',
23366     cls : '',
23367     href: '',
23368     video_url : '',
23369     
23370     // margin: '2%', not used
23371     
23372     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
23373
23374     
23375     // used by context menu
23376     friendly_name : 'Image with caption',
23377     deleteTitle : "Delete Image and Caption",
23378     
23379     contextMenu : function(toolbar)
23380     {
23381         
23382         var block = function() {
23383             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23384         };
23385         
23386         
23387         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23388         
23389         var syncValue = toolbar.editorcore.syncValue;
23390         
23391         var fields = {};
23392         
23393         return [
23394              {
23395                 xtype : 'TextItem',
23396                 text : "Source: ",
23397                 xns : rooui.Toolbar  //Boostrap?
23398             },
23399             {
23400                 xtype : 'Button',
23401                 text: 'Change Image URL',
23402                  
23403                 listeners : {
23404                     click: function (btn, state)
23405                     {
23406                         var b = block();
23407                         
23408                         Roo.MessageBox.show({
23409                             title : "Image Source URL",
23410                             msg : "Enter the url for the image",
23411                             buttons: Roo.MessageBox.OKCANCEL,
23412                             fn: function(btn, val){
23413                                 if (btn != 'ok') {
23414                                     return;
23415                                 }
23416                                 b.image_src = val;
23417                                 b.updateElement();
23418                                 syncValue();
23419                                 toolbar.editorcore.onEditorEvent();
23420                             },
23421                             minWidth:250,
23422                             prompt:true,
23423                             //multiline: multiline,
23424                             modal : true,
23425                             value : b.image_src
23426                         });
23427                     }
23428                 },
23429                 xns : rooui.Toolbar
23430             },
23431          
23432             {
23433                 xtype : 'Button',
23434                 text: 'Change Link URL',
23435                  
23436                 listeners : {
23437                     click: function (btn, state)
23438                     {
23439                         var b = block();
23440                         
23441                         Roo.MessageBox.show({
23442                             title : "Link URL",
23443                             msg : "Enter the url for the link - leave blank to have no link",
23444                             buttons: Roo.MessageBox.OKCANCEL,
23445                             fn: function(btn, val){
23446                                 if (btn != 'ok') {
23447                                     return;
23448                                 }
23449                                 b.href = val;
23450                                 b.updateElement();
23451                                 syncValue();
23452                                 toolbar.editorcore.onEditorEvent();
23453                             },
23454                             minWidth:250,
23455                             prompt:true,
23456                             //multiline: multiline,
23457                             modal : true,
23458                             value : b.href
23459                         });
23460                     }
23461                 },
23462                 xns : rooui.Toolbar
23463             },
23464             {
23465                 xtype : 'Button',
23466                 text: 'Show Video URL',
23467                  
23468                 listeners : {
23469                     click: function (btn, state)
23470                     {
23471                         Roo.MessageBox.alert("Video URL",
23472                             block().video_url == '' ? 'This image is not linked ot a video' :
23473                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23474                     }
23475                 },
23476                 xns : rooui.Toolbar
23477             },
23478             
23479             
23480             {
23481                 xtype : 'TextItem',
23482                 text : "Width: ",
23483                 xns : rooui.Toolbar  //Boostrap?
23484             },
23485             {
23486                 xtype : 'ComboBox',
23487                 allowBlank : false,
23488                 displayField : 'val',
23489                 editable : true,
23490                 listWidth : 100,
23491                 triggerAction : 'all',
23492                 typeAhead : true,
23493                 valueField : 'val',
23494                 width : 70,
23495                 name : 'width',
23496                 listeners : {
23497                     select : function (combo, r, index)
23498                     {
23499                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23500                         var b = block();
23501                         b.width = r.get('val');
23502                         b.updateElement();
23503                         syncValue();
23504                         toolbar.editorcore.onEditorEvent();
23505                     }
23506                 },
23507                 xns : rooui.form,
23508                 store : {
23509                     xtype : 'SimpleStore',
23510                     data : [
23511                         ['auto'],
23512                         ['50%'],
23513                         ['100%']
23514                     ],
23515                     fields : [ 'val'],
23516                     xns : Roo.data
23517                 }
23518             },
23519             {
23520                 xtype : 'TextItem',
23521                 text : "Align: ",
23522                 xns : rooui.Toolbar  //Boostrap?
23523             },
23524             {
23525                 xtype : 'ComboBox',
23526                 allowBlank : false,
23527                 displayField : 'val',
23528                 editable : true,
23529                 listWidth : 100,
23530                 triggerAction : 'all',
23531                 typeAhead : true,
23532                 valueField : 'val',
23533                 width : 70,
23534                 name : 'align',
23535                 listeners : {
23536                     select : function (combo, r, index)
23537                     {
23538                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23539                         var b = block();
23540                         b.align = r.get('val');
23541                         b.updateElement();
23542                         syncValue();
23543                         toolbar.editorcore.onEditorEvent();
23544                     }
23545                 },
23546                 xns : rooui.form,
23547                 store : {
23548                     xtype : 'SimpleStore',
23549                     data : [
23550                         ['left'],
23551                         ['right'],
23552                         ['center']
23553                     ],
23554                     fields : [ 'val'],
23555                     xns : Roo.data
23556                 }
23557             },
23558             
23559             
23560             {
23561                 xtype : 'Button',
23562                 text: 'Hide Caption',
23563                 name : 'caption_display',
23564                 pressed : false,
23565                 enableToggle : true,
23566                 setValue : function(v) {
23567                     this.toggle(v == 'block' ? false : true);
23568                 },
23569                 listeners : {
23570                     toggle: function (btn, state)
23571                     {
23572                         var b  = block();
23573                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
23574                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23575                         b.updateElement();
23576                         syncValue();
23577                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23578                         toolbar.editorcore.onEditorEvent();
23579                     }
23580                 },
23581                 xns : rooui.Toolbar
23582             }
23583         ];
23584         
23585     },
23586     /**
23587      * create a DomHelper friendly object - for use with
23588      * Roo.DomHelper.markup / overwrite / etc..
23589      */
23590     toObject : function()
23591     {
23592         var d = document.createElement('div');
23593         d.innerHTML = this.caption;
23594         
23595         var m = this.width == '50%' && this.align == 'center' ? '0 auto' : 0; 
23596         
23597         var img =   {
23598             tag : 'img',
23599             contenteditable : 'false',
23600             src : this.image_src,
23601             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
23602             style: {
23603                 width : 'auto',
23604                 'max-width': '100%',
23605                 margin : '0px' 
23606                 
23607                 
23608             }
23609         };
23610         /*
23611         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
23612                     '<a href="{2}">' + 
23613                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
23614                     '</a>' + 
23615                 '</div>',
23616         */
23617                 
23618         if (this.href.length > 0) {
23619             img = {
23620                 tag : 'a',
23621                 href: this.href,
23622                 contenteditable : 'true',
23623                 cn : [
23624                     img
23625                 ]
23626             };
23627         }
23628         
23629         
23630         if (this.video_url.length > 0) {
23631             img = {
23632                 tag : 'div',
23633                 cls : this.cls,
23634                 frameborder : 0,
23635                 allowfullscreen : true,
23636                 width : 420,  // these are for video tricks - that we replace the outer
23637                 height : 315,
23638                 src : this.video_url,
23639                 cn : [
23640                     img
23641                 ]
23642             };
23643         }
23644         
23645         return  {
23646             tag: 'figure',
23647             'data-block' : 'Figure',
23648             contenteditable : 'false',
23649             style : {
23650                 display: 'block',
23651                 float :  this.align ,
23652                 'max-width':  this.width,
23653                 width : 'auto',
23654                 margin:  m,
23655                 padding: '10px'
23656                 
23657             },
23658            
23659             
23660             align : this.align,
23661             cn : [
23662                 img,
23663               
23664                 {
23665                     tag: 'figcaption',
23666                     
23667                     style : {
23668                         'text-align': 'left',
23669                         'margin-top' : '16px',
23670                         'font-size' : '16px',
23671                         'line-height' : '24px',
23672                          display : this.caption_display
23673                     },
23674                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
23675                     cn : [
23676                         {
23677                             // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
23678                             tag : 'i',
23679                             contenteditable : true,
23680                             html : this.caption
23681                         }
23682                     ]
23683                     
23684                 }
23685             ]
23686         };
23687          
23688     },
23689     
23690     readElement : function(node)
23691     {
23692         // this should not really come from the link...
23693         this.video_url = this.getVal(node, 'div', 'src');
23694         this.cls = this.getVal(node, 'div', 'class');
23695         this.href = this.getVal(node, 'a', 'href');
23696         
23697         this.image_src = this.getVal(node, 'img', 'src');
23698          
23699         this.align = this.getVal(node, 'figure', 'align');
23700         this.caption = this.getVal(node, 'figcaption', 'html');
23701         // remove '<i>
23702         if (this.caption.trim().match(/^<i[^>]*>/i)) {
23703             this.caption = this.caption.trim().replace(/^<i[^>]*>/i, '').replace(/^<\/i>$/i, '');
23704         }
23705         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
23706         this.width = this.getVal(node, 'figure', 'style', 'max-width');
23707         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
23708         
23709     },
23710     removeNode : function()
23711     {
23712         return this.node;
23713     }
23714     
23715   
23716    
23717      
23718     
23719     
23720     
23721     
23722 })
23723
23724  
23725
23726 /**
23727  * @class Roo.htmleditor.BlockTable
23728  * Block that manages a table
23729  * 
23730  * @constructor
23731  * Create a new Filter.
23732  * @param {Object} config Configuration options
23733  */
23734
23735 Roo.htmleditor.BlockTable = function(cfg)
23736 {
23737     if (cfg.node) {
23738         this.readElement(cfg.node);
23739         this.updateElement(cfg.node);
23740     }
23741     Roo.apply(this, cfg);
23742     if (!cfg.node) {
23743         this.rows = [];
23744         for(var r = 0; r < this.no_row; r++) {
23745             this.rows[r] = [];
23746             for(var c = 0; c < this.no_col; c++) {
23747                 this.rows[r][c] = this.emptyCell();
23748             }
23749         }
23750     }
23751     
23752     
23753 }
23754 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
23755  
23756     rows : false,
23757     no_col : 1,
23758     no_row : 1,
23759     
23760     
23761     width: '100%',
23762     
23763     // used by context menu
23764     friendly_name : 'Table',
23765     deleteTitle : 'Delete Table',
23766     // context menu is drawn once..
23767     
23768     contextMenu : function(toolbar)
23769     {
23770         
23771         var block = function() {
23772             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23773         };
23774         
23775         
23776         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23777         
23778         var syncValue = toolbar.editorcore.syncValue;
23779         
23780         var fields = {};
23781         
23782         return [
23783             {
23784                 xtype : 'TextItem',
23785                 text : "Width: ",
23786                 xns : rooui.Toolbar  //Boostrap?
23787             },
23788             {
23789                 xtype : 'ComboBox',
23790                 allowBlank : false,
23791                 displayField : 'val',
23792                 editable : true,
23793                 listWidth : 100,
23794                 triggerAction : 'all',
23795                 typeAhead : true,
23796                 valueField : 'val',
23797                 width : 100,
23798                 name : 'width',
23799                 listeners : {
23800                     select : function (combo, r, index)
23801                     {
23802                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23803                         var b = block();
23804                         b.width = r.get('val');
23805                         b.updateElement();
23806                         syncValue();
23807                         toolbar.editorcore.onEditorEvent();
23808                     }
23809                 },
23810                 xns : rooui.form,
23811                 store : {
23812                     xtype : 'SimpleStore',
23813                     data : [
23814                         ['100%'],
23815                         ['auto']
23816                     ],
23817                     fields : [ 'val'],
23818                     xns : Roo.data
23819                 }
23820             },
23821             // -------- Cols
23822             
23823             {
23824                 xtype : 'TextItem',
23825                 text : "Columns: ",
23826                 xns : rooui.Toolbar  //Boostrap?
23827             },
23828          
23829             {
23830                 xtype : 'Button',
23831                 text: '-',
23832                 listeners : {
23833                     click : function (_self, e)
23834                     {
23835                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23836                         block().removeColumn();
23837                         syncValue();
23838                         toolbar.editorcore.onEditorEvent();
23839                     }
23840                 },
23841                 xns : rooui.Toolbar
23842             },
23843             {
23844                 xtype : 'Button',
23845                 text: '+',
23846                 listeners : {
23847                     click : function (_self, e)
23848                     {
23849                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23850                         block().addColumn();
23851                         syncValue();
23852                         toolbar.editorcore.onEditorEvent();
23853                     }
23854                 },
23855                 xns : rooui.Toolbar
23856             },
23857             // -------- ROWS
23858             {
23859                 xtype : 'TextItem',
23860                 text : "Rows: ",
23861                 xns : rooui.Toolbar  //Boostrap?
23862             },
23863          
23864             {
23865                 xtype : 'Button',
23866                 text: '-',
23867                 listeners : {
23868                     click : function (_self, e)
23869                     {
23870                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23871                         block().removeRow();
23872                         syncValue();
23873                         toolbar.editorcore.onEditorEvent();
23874                     }
23875                 },
23876                 xns : rooui.Toolbar
23877             },
23878             {
23879                 xtype : 'Button',
23880                 text: '+',
23881                 listeners : {
23882                     click : function (_self, e)
23883                     {
23884                         block().addRow();
23885                         syncValue();
23886                         toolbar.editorcore.onEditorEvent();
23887                     }
23888                 },
23889                 xns : rooui.Toolbar
23890             },
23891             // -------- ROWS
23892             {
23893                 xtype : 'Button',
23894                 text: 'Reset Column Widths',
23895                 listeners : {
23896                     
23897                     click : function (_self, e)
23898                     {
23899                         block().resetWidths();
23900                         syncValue();
23901                         toolbar.editorcore.onEditorEvent();
23902                     }
23903                 },
23904                 xns : rooui.Toolbar
23905             } 
23906             
23907             
23908             
23909         ];
23910         
23911     },
23912     
23913     
23914   /**
23915      * create a DomHelper friendly object - for use with
23916      * Roo.DomHelper.markup / overwrite / etc..
23917      * ?? should it be called with option to hide all editing features?
23918      */
23919     toObject : function()
23920     {
23921         
23922         var ret = {
23923             tag : 'table',
23924             contenteditable : 'false', // this stops cell selection from picking the table.
23925             'data-block' : 'Table',
23926             style : {
23927                 width:  this.width,
23928                 border : 'solid 1px #000', // ??? hard coded?
23929                 'border-collapse' : 'collapse' 
23930             },
23931             cn : [
23932                 { tag : 'tbody' , cn : [] }
23933             ]
23934         };
23935         
23936         // do we have a head = not really 
23937         var ncols = 0;
23938         Roo.each(this.rows, function( row ) {
23939             var tr = {
23940                 tag: 'tr',
23941                 style : {
23942                     margin: '6px',
23943                     border : 'solid 1px #000',
23944                     textAlign : 'left' 
23945                 },
23946                 cn : [ ]
23947             };
23948             
23949             ret.cn[0].cn.push(tr);
23950             // does the row have any properties? ?? height?
23951             var nc = 0;
23952             Roo.each(row, function( cell ) {
23953                 
23954                 var td = {
23955                     tag : 'td',
23956                     contenteditable :  'true',
23957                     'data-block' : 'Td',
23958                     html : cell.html,
23959                     style : cell.style
23960                 };
23961                 if (cell.colspan > 1) {
23962                     td.colspan = cell.colspan ;
23963                     nc += cell.colspan;
23964                 } else {
23965                     nc++;
23966                 }
23967                 if (cell.rowspan > 1) {
23968                     td.rowspan = cell.rowspan ;
23969                 }
23970                 
23971                 
23972                 // widths ?
23973                 tr.cn.push(td);
23974                     
23975                 
23976             }, this);
23977             ncols = Math.max(nc, ncols);
23978             
23979             
23980         }, this);
23981         // add the header row..
23982         
23983         ncols++;
23984          
23985         
23986         return ret;
23987          
23988     },
23989     
23990     readElement : function(node)
23991     {
23992         node  = node ? node : this.node ;
23993         this.width = this.getVal(node, true, 'style', 'width') || '100%';
23994         
23995         this.rows = [];
23996         this.no_row = 0;
23997         var trs = Array.from(node.rows);
23998         trs.forEach(function(tr) {
23999             var row =  [];
24000             this.rows.push(row);
24001             
24002             this.no_row++;
24003             var no_column = 0;
24004             Array.from(tr.cells).forEach(function(td) {
24005                 
24006                 var add = {
24007                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24008                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24009                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24010                     html : td.innerHTML
24011                 };
24012                 no_column += add.colspan;
24013                      
24014                 
24015                 row.push(add);
24016                 
24017                 
24018             },this);
24019             this.no_col = Math.max(this.no_col, no_column);
24020             
24021             
24022         },this);
24023         
24024         
24025     },
24026     normalizeRows: function()
24027     {
24028         var ret= [];
24029         var rid = -1;
24030         this.rows.forEach(function(row) {
24031             rid++;
24032             ret[rid] = [];
24033             row = this.normalizeRow(row);
24034             var cid = 0;
24035             row.forEach(function(c) {
24036                 while (typeof(ret[rid][cid]) != 'undefined') {
24037                     cid++;
24038                 }
24039                 if (typeof(ret[rid]) == 'undefined') {
24040                     ret[rid] = [];
24041                 }
24042                 ret[rid][cid] = c;
24043                 c.row = rid;
24044                 c.col = cid;
24045                 if (c.rowspan < 2) {
24046                     return;
24047                 }
24048                 
24049                 for(var i = 1 ;i < c.rowspan; i++) {
24050                     if (typeof(ret[rid+i]) == 'undefined') {
24051                         ret[rid+i] = [];
24052                     }
24053                     ret[rid+i][cid] = c;
24054                 }
24055             });
24056         }, this);
24057         return ret;
24058     
24059     },
24060     
24061     normalizeRow: function(row)
24062     {
24063         var ret= [];
24064         row.forEach(function(c) {
24065             if (c.colspan < 2) {
24066                 ret.push(c);
24067                 return;
24068             }
24069             for(var i =0 ;i < c.colspan; i++) {
24070                 ret.push(c);
24071             }
24072         });
24073         return ret;
24074     
24075     },
24076     
24077     deleteColumn : function(sel)
24078     {
24079         if (!sel || sel.type != 'col') {
24080             return;
24081         }
24082         if (this.no_col < 2) {
24083             return;
24084         }
24085         
24086         this.rows.forEach(function(row) {
24087             var cols = this.normalizeRow(row);
24088             var col = cols[sel.col];
24089             if (col.colspan > 1) {
24090                 col.colspan --;
24091             } else {
24092                 row.remove(col);
24093             }
24094             
24095         }, this);
24096         this.no_col--;
24097         
24098     },
24099     removeColumn : function()
24100     {
24101         this.deleteColumn({
24102             type: 'col',
24103             col : this.no_col-1
24104         });
24105         this.updateElement();
24106     },
24107     
24108      
24109     addColumn : function()
24110     {
24111         
24112         this.rows.forEach(function(row) {
24113             row.push(this.emptyCell());
24114            
24115         }, this);
24116         this.updateElement();
24117     },
24118     
24119     deleteRow : function(sel)
24120     {
24121         if (!sel || sel.type != 'row') {
24122             return;
24123         }
24124         
24125         if (this.no_row < 2) {
24126             return;
24127         }
24128         
24129         var rows = this.normalizeRows();
24130         
24131         
24132         rows[sel.row].forEach(function(col) {
24133             if (col.rowspan > 1) {
24134                 col.rowspan--;
24135             } else {
24136                 col.remove = 1; // flage it as removed.
24137             }
24138             
24139         }, this);
24140         var newrows = [];
24141         this.rows.forEach(function(row) {
24142             newrow = [];
24143             row.forEach(function(c) {
24144                 if (typeof(c.remove) == 'undefined') {
24145                     newrow.push(c);
24146                 }
24147                 
24148             });
24149             if (newrow.length > 0) {
24150                 newrows.push(row);
24151             }
24152         });
24153         this.rows =  newrows;
24154         
24155         
24156         
24157         this.no_row--;
24158         this.updateElement();
24159         
24160     },
24161     removeRow : function()
24162     {
24163         this.deleteRow({
24164             type: 'row',
24165             row : this.no_row-1
24166         });
24167         
24168     },
24169     
24170      
24171     addRow : function()
24172     {
24173         
24174         var row = [];
24175         for (var i = 0; i < this.no_col; i++ ) {
24176             
24177             row.push(this.emptyCell());
24178            
24179         }
24180         this.rows.push(row);
24181         this.updateElement();
24182         
24183     },
24184      
24185     // the default cell object... at present...
24186     emptyCell : function() {
24187         return (new Roo.htmleditor.BlockTd({})).toObject();
24188         
24189      
24190     },
24191     
24192     removeNode : function()
24193     {
24194         return this.node;
24195     },
24196     
24197     
24198     
24199     resetWidths : function()
24200     {
24201         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24202             var nn = Roo.htmleditor.Block.factory(n);
24203             nn.width = '';
24204             nn.updateElement(n);
24205         });
24206     }
24207     
24208     
24209     
24210     
24211 })
24212
24213 /**
24214  *
24215  * editing a TD?
24216  *
24217  * since selections really work on the table cell, then editing really should work from there
24218  *
24219  * The original plan was to support merging etc... - but that may not be needed yet..
24220  *
24221  * So this simple version will support:
24222  *   add/remove cols
24223  *   adjust the width +/-
24224  *   reset the width...
24225  *   
24226  *
24227  */
24228
24229
24230  
24231
24232 /**
24233  * @class Roo.htmleditor.BlockTable
24234  * Block that manages a table
24235  * 
24236  * @constructor
24237  * Create a new Filter.
24238  * @param {Object} config Configuration options
24239  */
24240
24241 Roo.htmleditor.BlockTd = function(cfg)
24242 {
24243     if (cfg.node) {
24244         this.readElement(cfg.node);
24245         this.updateElement(cfg.node);
24246     }
24247     Roo.apply(this, cfg);
24248      
24249     
24250     
24251 }
24252 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24253  
24254     node : false,
24255     
24256     width: '',
24257     textAlign : 'left',
24258     valign : 'top',
24259     
24260     colspan : 1,
24261     rowspan : 1,
24262     
24263     
24264     // used by context menu
24265     friendly_name : 'Table Cell',
24266     deleteTitle : false, // use our customer delete
24267     
24268     // context menu is drawn once..
24269     
24270     contextMenu : function(toolbar)
24271     {
24272         
24273         var cell = function() {
24274             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24275         };
24276         
24277         var table = function() {
24278             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24279         };
24280         
24281         var lr = false;
24282         var saveSel = function()
24283         {
24284             lr = toolbar.editorcore.getSelection().getRangeAt(0);
24285         }
24286         var restoreSel = function()
24287         {
24288             if (lr) {
24289                 (function() {
24290                     toolbar.editorcore.focus();
24291                     var cr = toolbar.editorcore.getSelection();
24292                     cr.removeAllRanges();
24293                     cr.addRange(lr);
24294                     toolbar.editorcore.onEditorEvent();
24295                 }).defer(10, this);
24296                 
24297                 
24298             }
24299         }
24300         
24301         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24302         
24303         var syncValue = toolbar.editorcore.syncValue;
24304         
24305         var fields = {};
24306         
24307         return [
24308             {
24309                 xtype : 'Button',
24310                 text : 'Edit Table',
24311                 listeners : {
24312                     click : function() {
24313                         var t = toolbar.tb.selectedNode.closest('table');
24314                         toolbar.editorcore.selectNode(t);
24315                         toolbar.editorcore.onEditorEvent();                        
24316                     }
24317                 }
24318                 
24319             },
24320               
24321            
24322              
24323             {
24324                 xtype : 'TextItem',
24325                 text : "Column Width: ",
24326                  xns : rooui.Toolbar 
24327                
24328             },
24329             {
24330                 xtype : 'Button',
24331                 text: '-',
24332                 listeners : {
24333                     click : function (_self, e)
24334                     {
24335                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24336                         cell().shrinkColumn();
24337                         syncValue();
24338                          toolbar.editorcore.onEditorEvent();
24339                     }
24340                 },
24341                 xns : rooui.Toolbar
24342             },
24343             {
24344                 xtype : 'Button',
24345                 text: '+',
24346                 listeners : {
24347                     click : function (_self, e)
24348                     {
24349                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24350                         cell().growColumn();
24351                         syncValue();
24352                         toolbar.editorcore.onEditorEvent();
24353                     }
24354                 },
24355                 xns : rooui.Toolbar
24356             },
24357             
24358             {
24359                 xtype : 'TextItem',
24360                 text : "Vertical Align: ",
24361                 xns : rooui.Toolbar  //Boostrap?
24362             },
24363             {
24364                 xtype : 'ComboBox',
24365                 allowBlank : false,
24366                 displayField : 'val',
24367                 editable : true,
24368                 listWidth : 100,
24369                 triggerAction : 'all',
24370                 typeAhead : true,
24371                 valueField : 'val',
24372                 width : 100,
24373                 name : 'valign',
24374                 listeners : {
24375                     select : function (combo, r, index)
24376                     {
24377                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24378                         var b = cell();
24379                         b.valign = r.get('val');
24380                         b.updateElement();
24381                         syncValue();
24382                         toolbar.editorcore.onEditorEvent();
24383                     }
24384                 },
24385                 xns : rooui.form,
24386                 store : {
24387                     xtype : 'SimpleStore',
24388                     data : [
24389                         ['top'],
24390                         ['middle'],
24391                         ['bottom'] // there are afew more... 
24392                     ],
24393                     fields : [ 'val'],
24394                     xns : Roo.data
24395                 }
24396             },
24397             
24398             {
24399                 xtype : 'TextItem',
24400                 text : "Merge Cells: ",
24401                  xns : rooui.Toolbar 
24402                
24403             },
24404             
24405             
24406             {
24407                 xtype : 'Button',
24408                 text: 'Right',
24409                 listeners : {
24410                     click : function (_self, e)
24411                     {
24412                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24413                         cell().mergeRight();
24414                         //block().growColumn();
24415                         syncValue();
24416                         toolbar.editorcore.onEditorEvent();
24417                     }
24418                 },
24419                 xns : rooui.Toolbar
24420             },
24421              
24422             {
24423                 xtype : 'Button',
24424                 text: 'Below',
24425                 listeners : {
24426                     click : function (_self, e)
24427                     {
24428                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24429                         cell().mergeBelow();
24430                         //block().growColumn();
24431                         syncValue();
24432                         toolbar.editorcore.onEditorEvent();
24433                     }
24434                 },
24435                 xns : rooui.Toolbar
24436             },
24437             {
24438                 xtype : 'TextItem',
24439                 text : "| ",
24440                  xns : rooui.Toolbar 
24441                
24442             },
24443             
24444             {
24445                 xtype : 'Button',
24446                 text: 'Split',
24447                 listeners : {
24448                     click : function (_self, e)
24449                     {
24450                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24451                         cell().split();
24452                         syncValue();
24453                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24454                         toolbar.editorcore.onEditorEvent();
24455                                              
24456                     }
24457                 },
24458                 xns : rooui.Toolbar
24459             },
24460             {
24461                 xtype : 'Fill',
24462                 xns : rooui.Toolbar 
24463                
24464             },
24465         
24466           
24467             {
24468                 xtype : 'Button',
24469                 text: 'Delete',
24470                  
24471                 xns : rooui.Toolbar,
24472                 menu : {
24473                     xtype : 'Menu',
24474                     xns : rooui.menu,
24475                     items : [
24476                         {
24477                             xtype : 'Item',
24478                             html: 'Column',
24479                             listeners : {
24480                                 click : function (_self, e)
24481                                 {
24482                                     var t = table();
24483                                     
24484                                     cell().deleteColumn();
24485                                     syncValue();
24486                                     toolbar.editorcore.selectNode(t.node);
24487                                     toolbar.editorcore.onEditorEvent();   
24488                                 }
24489                             },
24490                             xns : rooui.menu
24491                         },
24492                         {
24493                             xtype : 'Item',
24494                             html: 'Row',
24495                             listeners : {
24496                                 click : function (_self, e)
24497                                 {
24498                                     var t = table();
24499                                     cell().deleteRow();
24500                                     syncValue();
24501                                     
24502                                     toolbar.editorcore.selectNode(t.node);
24503                                     toolbar.editorcore.onEditorEvent();   
24504                                                          
24505                                 }
24506                             },
24507                             xns : rooui.menu
24508                         },
24509                        {
24510                             xtype : 'Separator',
24511                             xns : rooui.menu
24512                         },
24513                         {
24514                             xtype : 'Item',
24515                             html: 'Table',
24516                             listeners : {
24517                                 click : function (_self, e)
24518                                 {
24519                                     var t = table();
24520                                     var nn = t.node.nextSibling || t.node.previousSibling;
24521                                     t.node.parentNode.removeChild(t.node);
24522                                     if (nn) { 
24523                                         toolbar.editorcore.selectNode(nn, true);
24524                                     }
24525                                     toolbar.editorcore.onEditorEvent();   
24526                                                          
24527                                 }
24528                             },
24529                             xns : rooui.menu
24530                         }
24531                     ]
24532                 }
24533             }
24534             
24535             // align... << fixme
24536             
24537         ];
24538         
24539     },
24540     
24541     
24542   /**
24543      * create a DomHelper friendly object - for use with
24544      * Roo.DomHelper.markup / overwrite / etc..
24545      * ?? should it be called with option to hide all editing features?
24546      */
24547  /**
24548      * create a DomHelper friendly object - for use with
24549      * Roo.DomHelper.markup / overwrite / etc..
24550      * ?? should it be called with option to hide all editing features?
24551      */
24552     toObject : function()
24553     {
24554         
24555         var ret = {
24556             tag : 'td',
24557             contenteditable : 'true', // this stops cell selection from picking the table.
24558             'data-block' : 'Td',
24559             valign : this.valign,
24560             style : {  
24561                 'text-align' :  this.textAlign,
24562                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
24563                 'border-collapse' : 'collapse',
24564                 padding : '6px', // 8 for desktop / 4 for mobile
24565                 'vertical-align': this.valign
24566             },
24567             html : this.html
24568         };
24569         if (this.width != '') {
24570             ret.width = this.width;
24571             ret.style.width = this.width;
24572         }
24573         
24574         
24575         if (this.colspan > 1) {
24576             ret.colspan = this.colspan ;
24577         } 
24578         if (this.rowspan > 1) {
24579             ret.rowspan = this.rowspan ;
24580         }
24581         
24582            
24583         
24584         return ret;
24585          
24586     },
24587     
24588     readElement : function(node)
24589     {
24590         node  = node ? node : this.node ;
24591         this.width = node.style.width;
24592         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
24593         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
24594         this.html = node.innerHTML;
24595         
24596         
24597     },
24598      
24599     // the default cell object... at present...
24600     emptyCell : function() {
24601         return {
24602             colspan :  1,
24603             rowspan :  1,
24604             textAlign : 'left',
24605             html : "&nbsp;" // is this going to be editable now?
24606         };
24607      
24608     },
24609     
24610     removeNode : function()
24611     {
24612         return this.node.closest('table');
24613          
24614     },
24615     
24616     cellData : false,
24617     
24618     colWidths : false,
24619     
24620     toTableArray  : function()
24621     {
24622         var ret = [];
24623         var tab = this.node.closest('tr').closest('table');
24624         Array.from(tab.rows).forEach(function(r, ri){
24625             ret[ri] = [];
24626         });
24627         var rn = 0;
24628         this.colWidths = [];
24629         var all_auto = true;
24630         Array.from(tab.rows).forEach(function(r, ri){
24631             
24632             var cn = 0;
24633             Array.from(r.cells).forEach(function(ce, ci){
24634                 var c =  {
24635                     cell : ce,
24636                     row : rn,
24637                     col: cn,
24638                     colspan : ce.colSpan,
24639                     rowspan : ce.rowSpan
24640                 };
24641                 if (ce.isEqualNode(this.node)) {
24642                     this.cellData = c;
24643                 }
24644                 // if we have been filled up by a row?
24645                 if (typeof(ret[rn][cn]) != 'undefined') {
24646                     while(typeof(ret[rn][cn]) != 'undefined') {
24647                         cn++;
24648                     }
24649                     c.col = cn;
24650                 }
24651                 
24652                 if (typeof(this.colWidths[cn]) == 'undefined') {
24653                     this.colWidths[cn] =   ce.style.width;
24654                     if (this.colWidths[cn] != '') {
24655                         all_auto = false;
24656                     }
24657                 }
24658                 
24659                 
24660                 if (c.colspan < 2 && c.rowspan < 2 ) {
24661                     ret[rn][cn] = c;
24662                     cn++;
24663                     return;
24664                 }
24665                 for(var j = 0; j < c.rowspan; j++) {
24666                     if (typeof(ret[rn+j]) == 'undefined') {
24667                         continue; // we have a problem..
24668                     }
24669                     ret[rn+j][cn] = c;
24670                     for(var i = 0; i < c.colspan; i++) {
24671                         ret[rn+j][cn+i] = c;
24672                     }
24673                 }
24674                 
24675                 cn += c.colspan;
24676             }, this);
24677             rn++;
24678         }, this);
24679         
24680         // initalize widths.?
24681         // either all widths or no widths..
24682         if (all_auto) {
24683             this.colWidths[0] = false; // no widths flag.
24684         }
24685         
24686         
24687         return ret;
24688         
24689     },
24690     
24691     
24692     
24693     
24694     mergeRight: function()
24695     {
24696          
24697         // get the contents of the next cell along..
24698         var tr = this.node.closest('tr');
24699         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
24700         if (i >= tr.childNodes.length - 1) {
24701             return; // no cells on right to merge with.
24702         }
24703         var table = this.toTableArray();
24704         
24705         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
24706             return; // nothing right?
24707         }
24708         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
24709         // right cell - must be same rowspan and on the same row.
24710         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
24711             return; // right hand side is not same rowspan.
24712         }
24713         
24714         
24715         
24716         this.node.innerHTML += ' ' + rc.cell.innerHTML;
24717         tr.removeChild(rc.cell);
24718         this.colspan += rc.colspan;
24719         this.node.setAttribute('colspan', this.colspan);
24720
24721     },
24722     
24723     
24724     mergeBelow : function()
24725     {
24726         var table = this.toTableArray();
24727         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
24728             return; // no row below
24729         }
24730         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
24731             return; // nothing right?
24732         }
24733         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
24734         
24735         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
24736             return; // right hand side is not same rowspan.
24737         }
24738         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
24739         rc.cell.parentNode.removeChild(rc.cell);
24740         this.rowspan += rc.rowspan;
24741         this.node.setAttribute('rowspan', this.rowspan);
24742     },
24743     
24744     split: function()
24745     {
24746         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
24747             return;
24748         }
24749         var table = this.toTableArray();
24750         var cd = this.cellData;
24751         this.rowspan = 1;
24752         this.colspan = 1;
24753         
24754         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
24755             
24756             
24757             
24758             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
24759                 if (r == cd.row && c == cd.col) {
24760                     this.node.removeAttribute('rowspan');
24761                     this.node.removeAttribute('colspan');
24762                     continue;
24763                 }
24764                  
24765                 var ntd = this.node.cloneNode(); // which col/row should be 0..
24766                 ntd.removeAttribute('id'); //
24767                 //ntd.style.width  = '';
24768                 ntd.innerHTML = '';
24769                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
24770             }
24771             
24772         }
24773         this.redrawAllCells(table);
24774         
24775          
24776         
24777     },
24778     
24779     
24780     
24781     redrawAllCells: function(table)
24782     {
24783         
24784          
24785         var tab = this.node.closest('tr').closest('table');
24786         var ctr = tab.rows[0].parentNode;
24787         Array.from(tab.rows).forEach(function(r, ri){
24788             
24789             Array.from(r.cells).forEach(function(ce, ci){
24790                 ce.parentNode.removeChild(ce);
24791             });
24792             r.parentNode.removeChild(r);
24793         });
24794         for(var r = 0 ; r < table.length; r++) {
24795             var re = tab.rows[r];
24796             
24797             var re = tab.ownerDocument.createElement('tr');
24798             ctr.appendChild(re);
24799             for(var c = 0 ; c < table[r].length; c++) {
24800                 if (table[r][c].cell === false) {
24801                     continue;
24802                 }
24803                 
24804                 re.appendChild(table[r][c].cell);
24805                  
24806                 table[r][c].cell = false;
24807             }
24808         }
24809         
24810     },
24811     updateWidths : function(table)
24812     {
24813         for(var r = 0 ; r < table.length; r++) {
24814            
24815             for(var c = 0 ; c < table[r].length; c++) {
24816                 if (table[r][c].cell === false) {
24817                     continue;
24818                 }
24819                 
24820                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
24821                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
24822                     el.width = Math.floor(this.colWidths[c])  +'%';
24823                     el.updateElement(el.node);
24824                 }
24825                 table[r][c].cell = false; // done
24826             }
24827         }
24828     },
24829     normalizeWidths : function(table)
24830     {
24831     
24832         if (this.colWidths[0] === false) {
24833             var nw = 100.0 / this.colWidths.length;
24834             this.colWidths.forEach(function(w,i) {
24835                 this.colWidths[i] = nw;
24836             },this);
24837             return;
24838         }
24839     
24840         var t = 0, missing = [];
24841         
24842         this.colWidths.forEach(function(w,i) {
24843             //if you mix % and
24844             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
24845             var add =  this.colWidths[i];
24846             if (add > 0) {
24847                 t+=add;
24848                 return;
24849             }
24850             missing.push(i);
24851             
24852             
24853         },this);
24854         var nc = this.colWidths.length;
24855         if (missing.length) {
24856             var mult = (nc - missing.length) / (1.0 * nc);
24857             var t = mult * t;
24858             var ew = (100 -t) / (1.0 * missing.length);
24859             this.colWidths.forEach(function(w,i) {
24860                 if (w > 0) {
24861                     this.colWidths[i] = w * mult;
24862                     return;
24863                 }
24864                 
24865                 this.colWidths[i] = ew;
24866             }, this);
24867             // have to make up numbers..
24868              
24869         }
24870         // now we should have all the widths..
24871         
24872     
24873     },
24874     
24875     shrinkColumn : function()
24876     {
24877         var table = this.toTableArray();
24878         this.normalizeWidths(table);
24879         var col = this.cellData.col;
24880         var nw = this.colWidths[col] * 0.8;
24881         if (nw < 5) {
24882             return;
24883         }
24884         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24885         this.colWidths.forEach(function(w,i) {
24886             if (i == col) {
24887                  this.colWidths[i] = nw;
24888                 return;
24889             }
24890             this.colWidths[i] += otherAdd
24891         }, this);
24892         this.updateWidths(table);
24893          
24894     },
24895     growColumn : function()
24896     {
24897         var table = this.toTableArray();
24898         this.normalizeWidths(table);
24899         var col = this.cellData.col;
24900         var nw = this.colWidths[col] * 1.2;
24901         if (nw > 90) {
24902             return;
24903         }
24904         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24905         this.colWidths.forEach(function(w,i) {
24906             if (i == col) {
24907                 this.colWidths[i] = nw;
24908                 return;
24909             }
24910             this.colWidths[i] -= otherSub
24911         }, this);
24912         this.updateWidths(table);
24913          
24914     },
24915     deleteRow : function()
24916     {
24917         // delete this rows 'tr'
24918         // if any of the cells in this row have a rowspan > 1 && row!= this row..
24919         // then reduce the rowspan.
24920         var table = this.toTableArray();
24921         // this.cellData.row;
24922         for (var i =0;i< table[this.cellData.row].length ; i++) {
24923             var c = table[this.cellData.row][i];
24924             if (c.row != this.cellData.row) {
24925                 
24926                 c.rowspan--;
24927                 c.cell.setAttribute('rowspan', c.rowspan);
24928                 continue;
24929             }
24930             if (c.rowspan > 1) {
24931                 c.rowspan--;
24932                 c.cell.setAttribute('rowspan', c.rowspan);
24933             }
24934         }
24935         table.splice(this.cellData.row,1);
24936         this.redrawAllCells(table);
24937         
24938     },
24939     deleteColumn : function()
24940     {
24941         var table = this.toTableArray();
24942         
24943         for (var i =0;i< table.length ; i++) {
24944             var c = table[i][this.cellData.col];
24945             if (c.col != this.cellData.col) {
24946                 table[i][this.cellData.col].colspan--;
24947             } else if (c.colspan > 1) {
24948                 c.colspan--;
24949                 c.cell.setAttribute('colspan', c.colspan);
24950             }
24951             table[i].splice(this.cellData.col,1);
24952         }
24953         
24954         this.redrawAllCells(table);
24955     }
24956     
24957     
24958     
24959     
24960 })
24961
24962 //<script type="text/javascript">
24963
24964 /*
24965  * Based  Ext JS Library 1.1.1
24966  * Copyright(c) 2006-2007, Ext JS, LLC.
24967  * LGPL
24968  *
24969  */
24970  
24971 /**
24972  * @class Roo.HtmlEditorCore
24973  * @extends Roo.Component
24974  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24975  *
24976  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24977  */
24978
24979 Roo.HtmlEditorCore = function(config){
24980     
24981     
24982     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24983     
24984     
24985     this.addEvents({
24986         /**
24987          * @event initialize
24988          * Fires when the editor is fully initialized (including the iframe)
24989          * @param {Roo.HtmlEditorCore} this
24990          */
24991         initialize: true,
24992         /**
24993          * @event activate
24994          * Fires when the editor is first receives the focus. Any insertion must wait
24995          * until after this event.
24996          * @param {Roo.HtmlEditorCore} this
24997          */
24998         activate: true,
24999          /**
25000          * @event beforesync
25001          * Fires before the textarea is updated with content from the editor iframe. Return false
25002          * to cancel the sync.
25003          * @param {Roo.HtmlEditorCore} this
25004          * @param {String} html
25005          */
25006         beforesync: true,
25007          /**
25008          * @event beforepush
25009          * Fires before the iframe editor is updated with content from the textarea. Return false
25010          * to cancel the push.
25011          * @param {Roo.HtmlEditorCore} this
25012          * @param {String} html
25013          */
25014         beforepush: true,
25015          /**
25016          * @event sync
25017          * Fires when the textarea is updated with content from the editor iframe.
25018          * @param {Roo.HtmlEditorCore} this
25019          * @param {String} html
25020          */
25021         sync: true,
25022          /**
25023          * @event push
25024          * Fires when the iframe editor is updated with content from the textarea.
25025          * @param {Roo.HtmlEditorCore} this
25026          * @param {String} html
25027          */
25028         push: true,
25029         
25030         /**
25031          * @event editorevent
25032          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25033          * @param {Roo.HtmlEditorCore} this
25034          */
25035         editorevent: true 
25036          
25037         
25038     });
25039     
25040     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25041     
25042     // defaults : white / black...
25043     this.applyBlacklists();
25044     
25045     
25046     
25047 };
25048
25049
25050 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25051
25052
25053      /**
25054      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25055      */
25056     
25057     owner : false,
25058     
25059      /**
25060      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25061      *                        Roo.resizable.
25062      */
25063     resizable : false,
25064      /**
25065      * @cfg {Number} height (in pixels)
25066      */   
25067     height: 300,
25068    /**
25069      * @cfg {Number} width (in pixels)
25070      */   
25071     width: 500,
25072      /**
25073      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25074      *         if you are doing an email editor, this probably needs disabling, it's designed
25075      */
25076     autoClean: true,
25077     
25078     /**
25079      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25080      */
25081     enableBlocks : true,
25082     /**
25083      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25084      * 
25085      */
25086     stylesheets: false,
25087      /**
25088      * @cfg {String} language default en - language of text (usefull for rtl languages)
25089      * 
25090      */
25091     language: 'en',
25092     
25093     /**
25094      * @cfg {boolean} allowComments - default false - allow comments in HTML source
25095      *          - by default they are stripped - if you are editing email you may need this.
25096      */
25097     allowComments: false,
25098     // id of frame..
25099     frameId: false,
25100     
25101     // private properties
25102     validationEvent : false,
25103     deferHeight: true,
25104     initialized : false,
25105     activated : false,
25106     sourceEditMode : false,
25107     onFocus : Roo.emptyFn,
25108     iframePad:3,
25109     hideMode:'offsets',
25110     
25111     clearUp: true,
25112     
25113     // blacklist + whitelisted elements..
25114     black: false,
25115     white: false,
25116      
25117     bodyCls : '',
25118
25119     
25120     undoManager : false,
25121     /**
25122      * Protected method that will not generally be called directly. It
25123      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25124      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25125      */
25126     getDocMarkup : function(){
25127         // body styles..
25128         var st = '';
25129         
25130         // inherit styels from page...?? 
25131         if (this.stylesheets === false) {
25132             
25133             Roo.get(document.head).select('style').each(function(node) {
25134                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25135             });
25136             
25137             Roo.get(document.head).select('link').each(function(node) { 
25138                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25139             });
25140             
25141         } else if (!this.stylesheets.length) {
25142                 // simple..
25143                 st = '<style type="text/css">' +
25144                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25145                    '</style>';
25146         } else {
25147             for (var i in this.stylesheets) {
25148                 if (typeof(this.stylesheets[i]) != 'string') {
25149                     continue;
25150                 }
25151                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25152             }
25153             
25154         }
25155         
25156         st +=  '<style type="text/css">' +
25157             'IMG { cursor: pointer } ' +
25158         '</style>';
25159         
25160         st += '<meta name="google" content="notranslate">';
25161         
25162         var cls = 'notranslate roo-htmleditor-body';
25163         
25164         if(this.bodyCls.length){
25165             cls += ' ' + this.bodyCls;
25166         }
25167         
25168         return '<html  class="notranslate" translate="no"><head>' + st  +
25169             //<style type="text/css">' +
25170             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25171             //'</style>' +
25172             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25173     },
25174
25175     // private
25176     onRender : function(ct, position)
25177     {
25178         var _t = this;
25179         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25180         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25181         
25182         
25183         this.el.dom.style.border = '0 none';
25184         this.el.dom.setAttribute('tabIndex', -1);
25185         this.el.addClass('x-hidden hide');
25186         
25187         
25188         
25189         if(Roo.isIE){ // fix IE 1px bogus margin
25190             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25191         }
25192        
25193         
25194         this.frameId = Roo.id();
25195         
25196          
25197         
25198         var iframe = this.owner.wrap.createChild({
25199             tag: 'iframe',
25200             cls: 'form-control', // bootstrap..
25201             id: this.frameId,
25202             name: this.frameId,
25203             frameBorder : 'no',
25204             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25205         }, this.el
25206         );
25207         
25208         
25209         this.iframe = iframe.dom;
25210
25211         this.assignDocWin();
25212         
25213         this.doc.designMode = 'on';
25214        
25215         this.doc.open();
25216         this.doc.write(this.getDocMarkup());
25217         this.doc.close();
25218
25219         
25220         var task = { // must defer to wait for browser to be ready
25221             run : function(){
25222                 //console.log("run task?" + this.doc.readyState);
25223                 this.assignDocWin();
25224                 if(this.doc.body || this.doc.readyState == 'complete'){
25225                     try {
25226                         this.doc.designMode="on";
25227                         
25228                     } catch (e) {
25229                         return;
25230                     }
25231                     Roo.TaskMgr.stop(task);
25232                     this.initEditor.defer(10, this);
25233                 }
25234             },
25235             interval : 10,
25236             duration: 10000,
25237             scope: this
25238         };
25239         Roo.TaskMgr.start(task);
25240
25241     },
25242
25243     // private
25244     onResize : function(w, h)
25245     {
25246          Roo.log('resize: ' +w + ',' + h );
25247         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25248         if(!this.iframe){
25249             return;
25250         }
25251         if(typeof w == 'number'){
25252             
25253             this.iframe.style.width = w + 'px';
25254         }
25255         if(typeof h == 'number'){
25256             
25257             this.iframe.style.height = h + 'px';
25258             if(this.doc){
25259                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25260             }
25261         }
25262         
25263     },
25264
25265     /**
25266      * Toggles the editor between standard and source edit mode.
25267      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25268      */
25269     toggleSourceEdit : function(sourceEditMode){
25270         
25271         this.sourceEditMode = sourceEditMode === true;
25272         
25273         if(this.sourceEditMode){
25274  
25275             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
25276             
25277         }else{
25278             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25279             //this.iframe.className = '';
25280             this.deferFocus();
25281         }
25282         //this.setSize(this.owner.wrap.getSize());
25283         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25284     },
25285
25286     
25287   
25288
25289     /**
25290      * Protected method that will not generally be called directly. If you need/want
25291      * custom HTML cleanup, this is the method you should override.
25292      * @param {String} html The HTML to be cleaned
25293      * return {String} The cleaned HTML
25294      */
25295     cleanHtml : function(html)
25296     {
25297         html = String(html);
25298         if(html.length > 5){
25299             if(Roo.isSafari){ // strip safari nonsense
25300                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25301             }
25302         }
25303         if(html == '&nbsp;'){
25304             html = '';
25305         }
25306         return html;
25307     },
25308
25309     /**
25310      * HTML Editor -> Textarea
25311      * Protected method that will not generally be called directly. Syncs the contents
25312      * of the editor iframe with the textarea.
25313      */
25314     syncValue : function()
25315     {
25316         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25317         if(this.initialized){
25318             
25319             this.undoManager.addEvent();
25320
25321             
25322             var bd = (this.doc.body || this.doc.documentElement);
25323            
25324             
25325             var sel = this.win.getSelection();
25326             
25327             var div = document.createElement('div');
25328             div.innerHTML = bd.innerHTML;
25329             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25330             if (gtx.length > 0) {
25331                 var rm = gtx.item(0).parentNode;
25332                 rm.parentNode.removeChild(rm);
25333             }
25334             
25335            
25336             if (this.enableBlocks) {
25337                 new Roo.htmleditor.FilterBlock({ node : div });
25338             }
25339             //?? tidy?
25340             var tidy = new Roo.htmleditor.TidySerializer({
25341                 inner:  true
25342             });
25343             var html  = tidy.serialize(div);
25344             
25345             
25346             if(Roo.isSafari){
25347                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25348                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25349                 if(m && m[1]){
25350                     html = '<div style="'+m[0]+'">' + html + '</div>';
25351                 }
25352             }
25353             html = this.cleanHtml(html);
25354             // fix up the special chars.. normaly like back quotes in word...
25355             // however we do not want to do this with chinese..
25356             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25357                 
25358                 var cc = match.charCodeAt();
25359
25360                 // Get the character value, handling surrogate pairs
25361                 if (match.length == 2) {
25362                     // It's a surrogate pair, calculate the Unicode code point
25363                     var high = match.charCodeAt(0) - 0xD800;
25364                     var low  = match.charCodeAt(1) - 0xDC00;
25365                     cc = (high * 0x400) + low + 0x10000;
25366                 }  else if (
25367                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25368                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25369                     (cc >= 0xf900 && cc < 0xfb00 )
25370                 ) {
25371                         return match;
25372                 }  
25373          
25374                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25375                 return "&#" + cc + ";";
25376                 
25377                 
25378             });
25379             
25380             
25381              
25382             if(this.owner.fireEvent('beforesync', this, html) !== false){
25383                 this.el.dom.value = html;
25384                 this.owner.fireEvent('sync', this, html);
25385             }
25386         }
25387     },
25388
25389     /**
25390      * TEXTAREA -> EDITABLE
25391      * Protected method that will not generally be called directly. Pushes the value of the textarea
25392      * into the iframe editor.
25393      */
25394     pushValue : function()
25395     {
25396         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25397         if(this.initialized){
25398             var v = this.el.dom.value.trim();
25399             
25400             
25401             if(this.owner.fireEvent('beforepush', this, v) !== false){
25402                 var d = (this.doc.body || this.doc.documentElement);
25403                 d.innerHTML = v;
25404                  
25405                 this.el.dom.value = d.innerHTML;
25406                 this.owner.fireEvent('push', this, v);
25407             }
25408             if (this.autoClean) {
25409                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25410                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25411             }
25412             
25413             Roo.htmleditor.Block.initAll(this.doc.body);
25414             this.updateLanguage();
25415             
25416             var lc = this.doc.body.lastChild;
25417             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25418                 // add an extra line at the end.
25419                 this.doc.body.appendChild(this.doc.createElement('br'));
25420             }
25421             
25422             
25423         }
25424     },
25425
25426     // private
25427     deferFocus : function(){
25428         this.focus.defer(10, this);
25429     },
25430
25431     // doc'ed in Field
25432     focus : function(){
25433         if(this.win && !this.sourceEditMode){
25434             this.win.focus();
25435         }else{
25436             this.el.focus();
25437         }
25438     },
25439     
25440     assignDocWin: function()
25441     {
25442         var iframe = this.iframe;
25443         
25444          if(Roo.isIE){
25445             this.doc = iframe.contentWindow.document;
25446             this.win = iframe.contentWindow;
25447         } else {
25448 //            if (!Roo.get(this.frameId)) {
25449 //                return;
25450 //            }
25451 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25452 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25453             
25454             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25455                 return;
25456             }
25457             
25458             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25459             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25460         }
25461     },
25462     
25463     // private
25464     initEditor : function(){
25465         //console.log("INIT EDITOR");
25466         this.assignDocWin();
25467         
25468         
25469         
25470         this.doc.designMode="on";
25471         this.doc.open();
25472         this.doc.write(this.getDocMarkup());
25473         this.doc.close();
25474         
25475         var dbody = (this.doc.body || this.doc.documentElement);
25476         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25477         // this copies styles from the containing element into thsi one..
25478         // not sure why we need all of this..
25479         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25480         
25481         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25482         //ss['background-attachment'] = 'fixed'; // w3c
25483         dbody.bgProperties = 'fixed'; // ie
25484         dbody.setAttribute("translate", "no");
25485         
25486         //Roo.DomHelper.applyStyles(dbody, ss);
25487         Roo.EventManager.on(this.doc, {
25488              
25489             'mouseup': this.onEditorEvent,
25490             'dblclick': this.onEditorEvent,
25491             'click': this.onEditorEvent,
25492             'keyup': this.onEditorEvent,
25493             
25494             buffer:100,
25495             scope: this
25496         });
25497         Roo.EventManager.on(this.doc, {
25498             'paste': this.onPasteEvent,
25499             scope : this
25500         });
25501         if(Roo.isGecko){
25502             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25503         }
25504         //??? needed???
25505         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25506             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25507         }
25508         this.initialized = true;
25509
25510         
25511         // initialize special key events - enter
25512         new Roo.htmleditor.KeyEnter({core : this});
25513         
25514          
25515         
25516         this.owner.fireEvent('initialize', this);
25517         this.pushValue();
25518     },
25519     // this is to prevent a href clicks resulting in a redirect?
25520    
25521     onPasteEvent : function(e,v)
25522     {
25523         // I think we better assume paste is going to be a dirty load of rubish from word..
25524         
25525         // even pasting into a 'email version' of this widget will have to clean up that mess.
25526         var cd = (e.browserEvent.clipboardData || window.clipboardData);
25527         
25528         // check what type of paste - if it's an image, then handle it differently.
25529         if (cd.files.length > 0) {
25530             // pasting images?
25531             var urlAPI = (window.createObjectURL && window) || 
25532                 (window.URL && URL.revokeObjectURL && URL) || 
25533                 (window.webkitURL && webkitURL);
25534     
25535             var url = urlAPI.createObjectURL( cd.files[0]);
25536             this.insertAtCursor('<img src=" + url + ">');
25537             return false;
25538         }
25539         
25540         var html = cd.getData('text/html'); // clipboard event
25541         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25542         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25543         Roo.log(images);
25544         //Roo.log(imgs);
25545         // fixme..
25546         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
25547                        .map(function(g) { return g.toDataURL(); });
25548         
25549         
25550         html = this.cleanWordChars(html);
25551         
25552         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25553         
25554         
25555         var sn = this.getParentElement();
25556         // check if d contains a table, and prevent nesting??
25557         //Roo.log(d.getElementsByTagName('table'));
25558         //Roo.log(sn);
25559         //Roo.log(sn.closest('table'));
25560         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25561             e.preventDefault();
25562             this.insertAtCursor("You can not nest tables");
25563             //Roo.log("prevent?"); // fixme - 
25564             return false;
25565         }
25566         
25567         if (images.length > 0) {
25568             Roo.each(d.getElementsByTagName('img'), function(img, i) {
25569                 img.setAttribute('src', images[i]);
25570             });
25571         }
25572         if (this.autoClean) {
25573             new Roo.htmleditor.FilterStyleToTag({ node : d });
25574             new Roo.htmleditor.FilterAttributes({
25575                 node : d,
25576                 attrib_white : ['href', 'src', 'name', 'align'],
25577                 attrib_clean : ['href', 'src' ] 
25578             });
25579             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25580             // should be fonts..
25581             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
25582             new Roo.htmleditor.FilterParagraph({ node : d });
25583             new Roo.htmleditor.FilterSpan({ node : d });
25584             new Roo.htmleditor.FilterLongBr({ node : d });
25585         }
25586         if (this.enableBlocks) {
25587                 
25588             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25589                 if (img.closest('figure')) { // assume!! that it's aready
25590                     return;
25591                 }
25592                 var fig  = new Roo.htmleditor.BlockFigure({
25593                     image_src  : img.src
25594                 });
25595                 fig.updateElement(img); // replace it..
25596                 
25597             });
25598         }
25599         
25600         
25601         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
25602         if (this.enableBlocks) {
25603             Roo.htmleditor.Block.initAll(this.doc.body);
25604         }
25605         
25606         
25607         e.preventDefault();
25608         return false;
25609         // default behaveiour should be our local cleanup paste? (optional?)
25610         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25611         //this.owner.fireEvent('paste', e, v);
25612     },
25613     // private
25614     onDestroy : function(){
25615         
25616         
25617         
25618         if(this.rendered){
25619             
25620             //for (var i =0; i < this.toolbars.length;i++) {
25621             //    // fixme - ask toolbars for heights?
25622             //    this.toolbars[i].onDestroy();
25623            // }
25624             
25625             //this.wrap.dom.innerHTML = '';
25626             //this.wrap.remove();
25627         }
25628     },
25629
25630     // private
25631     onFirstFocus : function(){
25632         
25633         this.assignDocWin();
25634         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25635         
25636         this.activated = true;
25637          
25638     
25639         if(Roo.isGecko){ // prevent silly gecko errors
25640             this.win.focus();
25641             var s = this.win.getSelection();
25642             if(!s.focusNode || s.focusNode.nodeType != 3){
25643                 var r = s.getRangeAt(0);
25644                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25645                 r.collapse(true);
25646                 this.deferFocus();
25647             }
25648             try{
25649                 this.execCmd('useCSS', true);
25650                 this.execCmd('styleWithCSS', false);
25651             }catch(e){}
25652         }
25653         this.owner.fireEvent('activate', this);
25654     },
25655
25656     // private
25657     adjustFont: function(btn){
25658         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25659         //if(Roo.isSafari){ // safari
25660         //    adjust *= 2;
25661        // }
25662         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25663         if(Roo.isSafari){ // safari
25664             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25665             v =  (v < 10) ? 10 : v;
25666             v =  (v > 48) ? 48 : v;
25667             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25668             
25669         }
25670         
25671         
25672         v = Math.max(1, v+adjust);
25673         
25674         this.execCmd('FontSize', v  );
25675     },
25676
25677     onEditorEvent : function(e)
25678     {
25679          
25680         
25681         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25682             return; // we do not handle this.. (undo manager does..)
25683         }
25684         // in theory this detects if the last element is not a br, then we try and do that.
25685         // its so clicking in space at bottom triggers adding a br and moving the cursor.
25686         if (e &&
25687             e.target.nodeName == 'BODY' &&
25688             e.type == "mouseup" &&
25689             this.doc.body.lastChild
25690            ) {
25691             var lc = this.doc.body.lastChild;
25692             // gtx-trans is google translate plugin adding crap.
25693             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25694                 lc = lc.previousSibling;
25695             }
25696             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25697             // if last element is <BR> - then dont do anything.
25698             
25699                 var ns = this.doc.createElement('br');
25700                 this.doc.body.appendChild(ns);
25701                 range = this.doc.createRange();
25702                 range.setStartAfter(ns);
25703                 range.collapse(true);
25704                 var sel = this.win.getSelection();
25705                 sel.removeAllRanges();
25706                 sel.addRange(range);
25707             }
25708         }
25709         
25710         
25711         
25712         this.fireEditorEvent(e);
25713       //  this.updateToolbar();
25714         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25715     },
25716     
25717     fireEditorEvent: function(e)
25718     {
25719         this.owner.fireEvent('editorevent', this, e);
25720     },
25721
25722     insertTag : function(tg)
25723     {
25724         // could be a bit smarter... -> wrap the current selected tRoo..
25725         if (tg.toLowerCase() == 'span' ||
25726             tg.toLowerCase() == 'code' ||
25727             tg.toLowerCase() == 'sup' ||
25728             tg.toLowerCase() == 'sub' 
25729             ) {
25730             
25731             range = this.createRange(this.getSelection());
25732             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25733             wrappingNode.appendChild(range.extractContents());
25734             range.insertNode(wrappingNode);
25735
25736             return;
25737             
25738             
25739             
25740         }
25741         this.execCmd("formatblock",   tg);
25742         this.undoManager.addEvent(); 
25743     },
25744     
25745     insertText : function(txt)
25746     {
25747         
25748         
25749         var range = this.createRange();
25750         range.deleteContents();
25751                //alert(Sender.getAttribute('label'));
25752                
25753         range.insertNode(this.doc.createTextNode(txt));
25754         this.undoManager.addEvent();
25755     } ,
25756     
25757      
25758
25759     /**
25760      * Executes a Midas editor command on the editor document and performs necessary focus and
25761      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25762      * @param {String} cmd The Midas command
25763      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25764      */
25765     relayCmd : function(cmd, value)
25766     {
25767         
25768         switch (cmd) {
25769             case 'justifyleft':
25770             case 'justifyright':
25771             case 'justifycenter':
25772                 // if we are in a cell, then we will adjust the
25773                 var n = this.getParentElement();
25774                 var td = n.closest('td');
25775                 if (td) {
25776                     var bl = Roo.htmleditor.Block.factory(td);
25777                     bl.textAlign = cmd.replace('justify','');
25778                     bl.updateElement();
25779                     this.owner.fireEvent('editorevent', this);
25780                     return;
25781                 }
25782                 this.execCmd('styleWithCSS', true); // 
25783                 break;
25784             case 'bold':
25785             case 'italic':
25786                 // if there is no selection, then we insert, and set the curson inside it..
25787                 this.execCmd('styleWithCSS', false); 
25788                 break;
25789                 
25790         
25791             default:
25792                 break;
25793         }
25794         
25795         
25796         this.win.focus();
25797         this.execCmd(cmd, value);
25798         this.owner.fireEvent('editorevent', this);
25799         //this.updateToolbar();
25800         this.owner.deferFocus();
25801     },
25802
25803     /**
25804      * Executes a Midas editor command directly on the editor document.
25805      * For visual commands, you should use {@link #relayCmd} instead.
25806      * <b>This should only be called after the editor is initialized.</b>
25807      * @param {String} cmd The Midas command
25808      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25809      */
25810     execCmd : function(cmd, value){
25811         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25812         this.syncValue();
25813     },
25814  
25815  
25816    
25817     /**
25818      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25819      * to insert tRoo.
25820      * @param {String} text | dom node.. 
25821      */
25822     insertAtCursor : function(text)
25823     {
25824         
25825         if(!this.activated){
25826             return;
25827         }
25828          
25829         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25830             this.win.focus();
25831             
25832             
25833             // from jquery ui (MIT licenced)
25834             var range, node;
25835             var win = this.win;
25836             
25837             if (win.getSelection && win.getSelection().getRangeAt) {
25838                 
25839                 // delete the existing?
25840                 
25841                 this.createRange(this.getSelection()).deleteContents();
25842                 range = win.getSelection().getRangeAt(0);
25843                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25844                 range.insertNode(node);
25845                 range = range.cloneRange();
25846                 range.collapse(false);
25847                  
25848                 win.getSelection().removeAllRanges();
25849                 win.getSelection().addRange(range);
25850                 
25851                 
25852                 
25853             } else if (win.document.selection && win.document.selection.createRange) {
25854                 // no firefox support
25855                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25856                 win.document.selection.createRange().pasteHTML(txt);
25857             
25858             } else {
25859                 // no firefox support
25860                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25861                 this.execCmd('InsertHTML', txt);
25862             } 
25863             this.syncValue();
25864             
25865             this.deferFocus();
25866         }
25867     },
25868  // private
25869     mozKeyPress : function(e){
25870         if(e.ctrlKey){
25871             var c = e.getCharCode(), cmd;
25872           
25873             if(c > 0){
25874                 c = String.fromCharCode(c).toLowerCase();
25875                 switch(c){
25876                     case 'b':
25877                         cmd = 'bold';
25878                         break;
25879                     case 'i':
25880                         cmd = 'italic';
25881                         break;
25882                     
25883                     case 'u':
25884                         cmd = 'underline';
25885                         break;
25886                     
25887                     //case 'v':
25888                       //  this.cleanUpPaste.defer(100, this);
25889                       //  return;
25890                         
25891                 }
25892                 if(cmd){
25893                     
25894                     this.relayCmd(cmd);
25895                     //this.win.focus();
25896                     //this.execCmd(cmd);
25897                     //this.deferFocus();
25898                     e.preventDefault();
25899                 }
25900                 
25901             }
25902         }
25903     },
25904
25905     // private
25906     fixKeys : function(){ // load time branching for fastest keydown performance
25907         
25908         
25909         if(Roo.isIE){
25910             return function(e){
25911                 var k = e.getKey(), r;
25912                 if(k == e.TAB){
25913                     e.stopEvent();
25914                     r = this.doc.selection.createRange();
25915                     if(r){
25916                         r.collapse(true);
25917                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25918                         this.deferFocus();
25919                     }
25920                     return;
25921                 }
25922                 /// this is handled by Roo.htmleditor.KeyEnter
25923                  /*
25924                 if(k == e.ENTER){
25925                     r = this.doc.selection.createRange();
25926                     if(r){
25927                         var target = r.parentElement();
25928                         if(!target || target.tagName.toLowerCase() != 'li'){
25929                             e.stopEvent();
25930                             r.pasteHTML('<br/>');
25931                             r.collapse(false);
25932                             r.select();
25933                         }
25934                     }
25935                 }
25936                 */
25937                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25938                 //    this.cleanUpPaste.defer(100, this);
25939                 //    return;
25940                 //}
25941                 
25942                 
25943             };
25944         }else if(Roo.isOpera){
25945             return function(e){
25946                 var k = e.getKey();
25947                 if(k == e.TAB){
25948                     e.stopEvent();
25949                     this.win.focus();
25950                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25951                     this.deferFocus();
25952                 }
25953                
25954                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25955                 //    this.cleanUpPaste.defer(100, this);
25956                  //   return;
25957                 //}
25958                 
25959             };
25960         }else if(Roo.isSafari){
25961             return function(e){
25962                 var k = e.getKey();
25963                 
25964                 if(k == e.TAB){
25965                     e.stopEvent();
25966                     this.execCmd('InsertText','\t');
25967                     this.deferFocus();
25968                     return;
25969                 }
25970                  this.mozKeyPress(e);
25971                 
25972                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25973                  //   this.cleanUpPaste.defer(100, this);
25974                  //   return;
25975                // }
25976                 
25977              };
25978         }
25979     }(),
25980     
25981     getAllAncestors: function()
25982     {
25983         var p = this.getSelectedNode();
25984         var a = [];
25985         if (!p) {
25986             a.push(p); // push blank onto stack..
25987             p = this.getParentElement();
25988         }
25989         
25990         
25991         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25992             a.push(p);
25993             p = p.parentNode;
25994         }
25995         a.push(this.doc.body);
25996         return a;
25997     },
25998     lastSel : false,
25999     lastSelNode : false,
26000     
26001     
26002     getSelection : function() 
26003     {
26004         this.assignDocWin();
26005         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26006     },
26007     /**
26008      * Select a dom node
26009      * @param {DomElement} node the node to select
26010      */
26011     selectNode : function(node, collapse)
26012     {
26013         var nodeRange = node.ownerDocument.createRange();
26014         try {
26015             nodeRange.selectNode(node);
26016         } catch (e) {
26017             nodeRange.selectNodeContents(node);
26018         }
26019         if (collapse === true) {
26020             nodeRange.collapse(true);
26021         }
26022         //
26023         var s = this.win.getSelection();
26024         s.removeAllRanges();
26025         s.addRange(nodeRange);
26026     },
26027     
26028     getSelectedNode: function() 
26029     {
26030         // this may only work on Gecko!!!
26031         
26032         // should we cache this!!!!
26033         
26034          
26035          
26036         var range = this.createRange(this.getSelection()).cloneRange();
26037         
26038         if (Roo.isIE) {
26039             var parent = range.parentElement();
26040             while (true) {
26041                 var testRange = range.duplicate();
26042                 testRange.moveToElementText(parent);
26043                 if (testRange.inRange(range)) {
26044                     break;
26045                 }
26046                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26047                     break;
26048                 }
26049                 parent = parent.parentElement;
26050             }
26051             return parent;
26052         }
26053         
26054         // is ancestor a text element.
26055         var ac =  range.commonAncestorContainer;
26056         if (ac.nodeType == 3) {
26057             ac = ac.parentNode;
26058         }
26059         
26060         var ar = ac.childNodes;
26061          
26062         var nodes = [];
26063         var other_nodes = [];
26064         var has_other_nodes = false;
26065         for (var i=0;i<ar.length;i++) {
26066             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26067                 continue;
26068             }
26069             // fullly contained node.
26070             
26071             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26072                 nodes.push(ar[i]);
26073                 continue;
26074             }
26075             
26076             // probably selected..
26077             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26078                 other_nodes.push(ar[i]);
26079                 continue;
26080             }
26081             // outer..
26082             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26083                 continue;
26084             }
26085             
26086             
26087             has_other_nodes = true;
26088         }
26089         if (!nodes.length && other_nodes.length) {
26090             nodes= other_nodes;
26091         }
26092         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26093             return false;
26094         }
26095         
26096         return nodes[0];
26097     },
26098     
26099     
26100     createRange: function(sel)
26101     {
26102         // this has strange effects when using with 
26103         // top toolbar - not sure if it's a great idea.
26104         //this.editor.contentWindow.focus();
26105         if (typeof sel != "undefined") {
26106             try {
26107                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26108             } catch(e) {
26109                 return this.doc.createRange();
26110             }
26111         } else {
26112             return this.doc.createRange();
26113         }
26114     },
26115     getParentElement: function()
26116     {
26117         
26118         this.assignDocWin();
26119         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26120         
26121         var range = this.createRange(sel);
26122          
26123         try {
26124             var p = range.commonAncestorContainer;
26125             while (p.nodeType == 3) { // text node
26126                 p = p.parentNode;
26127             }
26128             return p;
26129         } catch (e) {
26130             return null;
26131         }
26132     
26133     },
26134     /***
26135      *
26136      * Range intersection.. the hard stuff...
26137      *  '-1' = before
26138      *  '0' = hits..
26139      *  '1' = after.
26140      *         [ -- selected range --- ]
26141      *   [fail]                        [fail]
26142      *
26143      *    basically..
26144      *      if end is before start or  hits it. fail.
26145      *      if start is after end or hits it fail.
26146      *
26147      *   if either hits (but other is outside. - then it's not 
26148      *   
26149      *    
26150      **/
26151     
26152     
26153     // @see http://www.thismuchiknow.co.uk/?p=64.
26154     rangeIntersectsNode : function(range, node)
26155     {
26156         var nodeRange = node.ownerDocument.createRange();
26157         try {
26158             nodeRange.selectNode(node);
26159         } catch (e) {
26160             nodeRange.selectNodeContents(node);
26161         }
26162     
26163         var rangeStartRange = range.cloneRange();
26164         rangeStartRange.collapse(true);
26165     
26166         var rangeEndRange = range.cloneRange();
26167         rangeEndRange.collapse(false);
26168     
26169         var nodeStartRange = nodeRange.cloneRange();
26170         nodeStartRange.collapse(true);
26171     
26172         var nodeEndRange = nodeRange.cloneRange();
26173         nodeEndRange.collapse(false);
26174     
26175         return rangeStartRange.compareBoundaryPoints(
26176                  Range.START_TO_START, nodeEndRange) == -1 &&
26177                rangeEndRange.compareBoundaryPoints(
26178                  Range.START_TO_START, nodeStartRange) == 1;
26179         
26180          
26181     },
26182     rangeCompareNode : function(range, node)
26183     {
26184         var nodeRange = node.ownerDocument.createRange();
26185         try {
26186             nodeRange.selectNode(node);
26187         } catch (e) {
26188             nodeRange.selectNodeContents(node);
26189         }
26190         
26191         
26192         range.collapse(true);
26193     
26194         nodeRange.collapse(true);
26195      
26196         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26197         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26198          
26199         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26200         
26201         var nodeIsBefore   =  ss == 1;
26202         var nodeIsAfter    = ee == -1;
26203         
26204         if (nodeIsBefore && nodeIsAfter) {
26205             return 0; // outer
26206         }
26207         if (!nodeIsBefore && nodeIsAfter) {
26208             return 1; //right trailed.
26209         }
26210         
26211         if (nodeIsBefore && !nodeIsAfter) {
26212             return 2;  // left trailed.
26213         }
26214         // fully contined.
26215         return 3;
26216     },
26217  
26218     cleanWordChars : function(input) {// change the chars to hex code
26219         
26220        var swapCodes  = [ 
26221             [    8211, "&#8211;" ], 
26222             [    8212, "&#8212;" ], 
26223             [    8216,  "'" ],  
26224             [    8217, "'" ],  
26225             [    8220, '"' ],  
26226             [    8221, '"' ],  
26227             [    8226, "*" ],  
26228             [    8230, "..." ]
26229         ]; 
26230         var output = input;
26231         Roo.each(swapCodes, function(sw) { 
26232             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26233             
26234             output = output.replace(swapper, sw[1]);
26235         });
26236         
26237         return output;
26238     },
26239     
26240      
26241     
26242         
26243     
26244     cleanUpChild : function (node)
26245     {
26246         
26247         new Roo.htmleditor.FilterComment({node : node});
26248         new Roo.htmleditor.FilterAttributes({
26249                 node : node,
26250                 attrib_black : this.ablack,
26251                 attrib_clean : this.aclean,
26252                 style_white : this.cwhite,
26253                 style_black : this.cblack
26254         });
26255         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26256         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26257          
26258         
26259     },
26260     
26261     /**
26262      * Clean up MS wordisms...
26263      * @deprecated - use filter directly
26264      */
26265     cleanWord : function(node)
26266     {
26267         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26268         
26269     },
26270    
26271     
26272     /**
26273
26274      * @deprecated - use filters
26275      */
26276     cleanTableWidths : function(node)
26277     {
26278         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26279         
26280  
26281     },
26282     
26283      
26284         
26285     applyBlacklists : function()
26286     {
26287         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26288         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26289         
26290         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
26291         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
26292         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
26293         
26294         this.white = [];
26295         this.black = [];
26296         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26297             if (b.indexOf(tag) > -1) {
26298                 return;
26299             }
26300             this.white.push(tag);
26301             
26302         }, this);
26303         
26304         Roo.each(w, function(tag) {
26305             if (b.indexOf(tag) > -1) {
26306                 return;
26307             }
26308             if (this.white.indexOf(tag) > -1) {
26309                 return;
26310             }
26311             this.white.push(tag);
26312             
26313         }, this);
26314         
26315         
26316         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26317             if (w.indexOf(tag) > -1) {
26318                 return;
26319             }
26320             this.black.push(tag);
26321             
26322         }, this);
26323         
26324         Roo.each(b, function(tag) {
26325             if (w.indexOf(tag) > -1) {
26326                 return;
26327             }
26328             if (this.black.indexOf(tag) > -1) {
26329                 return;
26330             }
26331             this.black.push(tag);
26332             
26333         }, this);
26334         
26335         
26336         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26337         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26338         
26339         this.cwhite = [];
26340         this.cblack = [];
26341         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26342             if (b.indexOf(tag) > -1) {
26343                 return;
26344             }
26345             this.cwhite.push(tag);
26346             
26347         }, this);
26348         
26349         Roo.each(w, function(tag) {
26350             if (b.indexOf(tag) > -1) {
26351                 return;
26352             }
26353             if (this.cwhite.indexOf(tag) > -1) {
26354                 return;
26355             }
26356             this.cwhite.push(tag);
26357             
26358         }, this);
26359         
26360         
26361         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26362             if (w.indexOf(tag) > -1) {
26363                 return;
26364             }
26365             this.cblack.push(tag);
26366             
26367         }, this);
26368         
26369         Roo.each(b, function(tag) {
26370             if (w.indexOf(tag) > -1) {
26371                 return;
26372             }
26373             if (this.cblack.indexOf(tag) > -1) {
26374                 return;
26375             }
26376             this.cblack.push(tag);
26377             
26378         }, this);
26379     },
26380     
26381     setStylesheets : function(stylesheets)
26382     {
26383         if(typeof(stylesheets) == 'string'){
26384             Roo.get(this.iframe.contentDocument.head).createChild({
26385                 tag : 'link',
26386                 rel : 'stylesheet',
26387                 type : 'text/css',
26388                 href : stylesheets
26389             });
26390             
26391             return;
26392         }
26393         var _this = this;
26394      
26395         Roo.each(stylesheets, function(s) {
26396             if(!s.length){
26397                 return;
26398             }
26399             
26400             Roo.get(_this.iframe.contentDocument.head).createChild({
26401                 tag : 'link',
26402                 rel : 'stylesheet',
26403                 type : 'text/css',
26404                 href : s
26405             });
26406         });
26407
26408         
26409     },
26410     
26411     
26412     updateLanguage : function()
26413     {
26414         if (!this.iframe || !this.iframe.contentDocument) {
26415             return;
26416         }
26417         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26418     },
26419     
26420     
26421     removeStylesheets : function()
26422     {
26423         var _this = this;
26424         
26425         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26426             s.remove();
26427         });
26428     },
26429     
26430     setStyle : function(style)
26431     {
26432         Roo.get(this.iframe.contentDocument.head).createChild({
26433             tag : 'style',
26434             type : 'text/css',
26435             html : style
26436         });
26437
26438         return;
26439     }
26440     
26441     // hide stuff that is not compatible
26442     /**
26443      * @event blur
26444      * @hide
26445      */
26446     /**
26447      * @event change
26448      * @hide
26449      */
26450     /**
26451      * @event focus
26452      * @hide
26453      */
26454     /**
26455      * @event specialkey
26456      * @hide
26457      */
26458     /**
26459      * @cfg {String} fieldClass @hide
26460      */
26461     /**
26462      * @cfg {String} focusClass @hide
26463      */
26464     /**
26465      * @cfg {String} autoCreate @hide
26466      */
26467     /**
26468      * @cfg {String} inputType @hide
26469      */
26470     /**
26471      * @cfg {String} invalidClass @hide
26472      */
26473     /**
26474      * @cfg {String} invalidText @hide
26475      */
26476     /**
26477      * @cfg {String} msgFx @hide
26478      */
26479     /**
26480      * @cfg {String} validateOnBlur @hide
26481      */
26482 });
26483
26484 Roo.HtmlEditorCore.white = [
26485         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26486         
26487        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
26488        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
26489        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
26490        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
26491        'TABLE',   'UL',         'XMP', 
26492        
26493        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
26494       'THEAD',   'TR', 
26495      
26496       'DIR', 'MENU', 'OL', 'UL', 'DL',
26497        
26498       'EMBED',  'OBJECT'
26499 ];
26500
26501
26502 Roo.HtmlEditorCore.black = [
26503     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26504         'APPLET', // 
26505         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
26506         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
26507         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
26508         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
26509         //'FONT' // CLEAN LATER..
26510         'COLGROUP', 'COL'   // messy tables.
26511         
26512         
26513 ];
26514 Roo.HtmlEditorCore.clean = [ // ?? needed???
26515      'SCRIPT', 'STYLE', 'TITLE', 'XML'
26516 ];
26517 Roo.HtmlEditorCore.tag_remove = [
26518     'FONT', 'TBODY'  
26519 ];
26520 // attributes..
26521
26522 Roo.HtmlEditorCore.ablack = [
26523     'on'
26524 ];
26525     
26526 Roo.HtmlEditorCore.aclean = [ 
26527     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26528 ];
26529
26530 // protocols..
26531 Roo.HtmlEditorCore.pwhite= [
26532         'http',  'https',  'mailto'
26533 ];
26534
26535 // white listed style attributes.
26536 Roo.HtmlEditorCore.cwhite= [
26537       //  'text-align', /// default is to allow most things..
26538       
26539          
26540 //        'font-size'//??
26541 ];
26542
26543 // black listed style attributes.
26544 Roo.HtmlEditorCore.cblack= [
26545       //  'font-size' -- this can be set by the project 
26546 ];
26547
26548
26549
26550
26551     //<script type="text/javascript">
26552
26553 /*
26554  * Ext JS Library 1.1.1
26555  * Copyright(c) 2006-2007, Ext JS, LLC.
26556  * Licence LGPL
26557  * 
26558  */
26559  
26560  
26561 Roo.form.HtmlEditor = function(config){
26562     
26563     
26564     
26565     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26566     
26567     if (!this.toolbars) {
26568         this.toolbars = [];
26569     }
26570     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26571     
26572     
26573 };
26574
26575 /**
26576  * @class Roo.form.HtmlEditor
26577  * @extends Roo.form.Field
26578  * Provides a lightweight HTML Editor component.
26579  *
26580  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26581  * 
26582  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26583  * supported by this editor.</b><br/><br/>
26584  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26585  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26586  */
26587 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26588     /**
26589      * @cfg {Boolean} clearUp
26590      */
26591     clearUp : true,
26592       /**
26593      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26594      */
26595     toolbars : false,
26596    
26597      /**
26598      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26599      *                        Roo.resizable.
26600      */
26601     resizable : false,
26602      /**
26603      * @cfg {Number} height (in pixels)
26604      */   
26605     height: 300,
26606    /**
26607      * @cfg {Number} width (in pixels)
26608      */   
26609     width: 500,
26610     
26611     /**
26612      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
26613      * 
26614      */
26615     stylesheets: false,
26616     
26617     
26618      /**
26619      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26620      * 
26621      */
26622     cblack: false,
26623     /**
26624      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26625      * 
26626      */
26627     cwhite: false,
26628     
26629      /**
26630      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26631      * 
26632      */
26633     black: false,
26634     /**
26635      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26636      * 
26637      */
26638     white: false,
26639     /**
26640      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26641      */
26642     allowComments: false,
26643     /**
26644      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26645      */
26646     enableBlocks : true,
26647     
26648     /**
26649      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26650      *         if you are doing an email editor, this probably needs disabling, it's designed
26651      */
26652     autoClean: true,
26653     /**
26654      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
26655      */
26656     bodyCls : '',
26657     /**
26658      * @cfg {String} language default en - language of text (usefull for rtl languages)
26659      * 
26660      */
26661     language: 'en',
26662     
26663      
26664     // id of frame..
26665     frameId: false,
26666     
26667     // private properties
26668     validationEvent : false,
26669     deferHeight: true,
26670     initialized : false,
26671     activated : false,
26672     
26673     onFocus : Roo.emptyFn,
26674     iframePad:3,
26675     hideMode:'offsets',
26676     
26677     actionMode : 'container', // defaults to hiding it...
26678     
26679     defaultAutoCreate : { // modified by initCompnoent..
26680         tag: "textarea",
26681         style:"width:500px;height:300px;",
26682         autocomplete: "new-password"
26683     },
26684
26685     // private
26686     initComponent : function(){
26687         this.addEvents({
26688             /**
26689              * @event initialize
26690              * Fires when the editor is fully initialized (including the iframe)
26691              * @param {HtmlEditor} this
26692              */
26693             initialize: true,
26694             /**
26695              * @event activate
26696              * Fires when the editor is first receives the focus. Any insertion must wait
26697              * until after this event.
26698              * @param {HtmlEditor} this
26699              */
26700             activate: true,
26701              /**
26702              * @event beforesync
26703              * Fires before the textarea is updated with content from the editor iframe. Return false
26704              * to cancel the sync.
26705              * @param {HtmlEditor} this
26706              * @param {String} html
26707              */
26708             beforesync: true,
26709              /**
26710              * @event beforepush
26711              * Fires before the iframe editor is updated with content from the textarea. Return false
26712              * to cancel the push.
26713              * @param {HtmlEditor} this
26714              * @param {String} html
26715              */
26716             beforepush: true,
26717              /**
26718              * @event sync
26719              * Fires when the textarea is updated with content from the editor iframe.
26720              * @param {HtmlEditor} this
26721              * @param {String} html
26722              */
26723             sync: true,
26724              /**
26725              * @event push
26726              * Fires when the iframe editor is updated with content from the textarea.
26727              * @param {HtmlEditor} this
26728              * @param {String} html
26729              */
26730             push: true,
26731              /**
26732              * @event editmodechange
26733              * Fires when the editor switches edit modes
26734              * @param {HtmlEditor} this
26735              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26736              */
26737             editmodechange: true,
26738             /**
26739              * @event editorevent
26740              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26741              * @param {HtmlEditor} this
26742              */
26743             editorevent: true,
26744             /**
26745              * @event firstfocus
26746              * Fires when on first focus - needed by toolbars..
26747              * @param {HtmlEditor} this
26748              */
26749             firstfocus: true,
26750             /**
26751              * @event autosave
26752              * Auto save the htmlEditor value as a file into Events
26753              * @param {HtmlEditor} this
26754              */
26755             autosave: true,
26756             /**
26757              * @event savedpreview
26758              * preview the saved version of htmlEditor
26759              * @param {HtmlEditor} this
26760              */
26761             savedpreview: true,
26762             
26763             /**
26764             * @event stylesheetsclick
26765             * Fires when press the Sytlesheets button
26766             * @param {Roo.HtmlEditorCore} this
26767             */
26768             stylesheetsclick: true,
26769             /**
26770             * @event paste
26771             * Fires when press user pastes into the editor
26772             * @param {Roo.HtmlEditorCore} this
26773             */
26774             paste: true 
26775         });
26776         this.defaultAutoCreate =  {
26777             tag: "textarea",
26778             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26779             autocomplete: "new-password"
26780         };
26781     },
26782
26783     /**
26784      * Protected method that will not generally be called directly. It
26785      * is called when the editor creates its toolbar. Override this method if you need to
26786      * add custom toolbar buttons.
26787      * @param {HtmlEditor} editor
26788      */
26789     createToolbar : function(editor){
26790         Roo.log("create toolbars");
26791         if (!editor.toolbars || !editor.toolbars.length) {
26792             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26793         }
26794         
26795         for (var i =0 ; i < editor.toolbars.length;i++) {
26796             editor.toolbars[i] = Roo.factory(
26797                     typeof(editor.toolbars[i]) == 'string' ?
26798                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26799                 Roo.form.HtmlEditor);
26800             editor.toolbars[i].init(editor);
26801         }
26802          
26803         
26804     },
26805     /**
26806      * get the Context selected node
26807      * @returns {DomElement|boolean} selected node if active or false if none
26808      * 
26809      */
26810     getSelectedNode : function()
26811     {
26812         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
26813             return false;
26814         }
26815         return this.toolbars[1].tb.selectedNode;
26816     
26817     },
26818     // private
26819     onRender : function(ct, position)
26820     {
26821         var _t = this;
26822         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26823         
26824         this.wrap = this.el.wrap({
26825             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26826         });
26827         
26828         this.editorcore.onRender(ct, position);
26829          
26830         if (this.resizable) {
26831             this.resizeEl = new Roo.Resizable(this.wrap, {
26832                 pinned : true,
26833                 wrap: true,
26834                 dynamic : true,
26835                 minHeight : this.height,
26836                 height: this.height,
26837                 handles : this.resizable,
26838                 width: this.width,
26839                 listeners : {
26840                     resize : function(r, w, h) {
26841                         _t.onResize(w,h); // -something
26842                     }
26843                 }
26844             });
26845             
26846         }
26847         this.createToolbar(this);
26848        
26849         
26850         if(!this.width){
26851             this.setSize(this.wrap.getSize());
26852         }
26853         if (this.resizeEl) {
26854             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26855             // should trigger onReize..
26856         }
26857         
26858         this.keyNav = new Roo.KeyNav(this.el, {
26859             
26860             "tab" : function(e){
26861                 e.preventDefault();
26862                 
26863                 var value = this.getValue();
26864                 
26865                 var start = this.el.dom.selectionStart;
26866                 var end = this.el.dom.selectionEnd;
26867                 
26868                 if(!e.shiftKey){
26869                     
26870                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26871                     this.el.dom.setSelectionRange(end + 1, end + 1);
26872                     return;
26873                 }
26874                 
26875                 var f = value.substring(0, start).split("\t");
26876                 
26877                 if(f.pop().length != 0){
26878                     return;
26879                 }
26880                 
26881                 this.setValue(f.join("\t") + value.substring(end));
26882                 this.el.dom.setSelectionRange(start - 1, start - 1);
26883                 
26884             },
26885             
26886             "home" : function(e){
26887                 e.preventDefault();
26888                 
26889                 var curr = this.el.dom.selectionStart;
26890                 var lines = this.getValue().split("\n");
26891                 
26892                 if(!lines.length){
26893                     return;
26894                 }
26895                 
26896                 if(e.ctrlKey){
26897                     this.el.dom.setSelectionRange(0, 0);
26898                     return;
26899                 }
26900                 
26901                 var pos = 0;
26902                 
26903                 for (var i = 0; i < lines.length;i++) {
26904                     pos += lines[i].length;
26905                     
26906                     if(i != 0){
26907                         pos += 1;
26908                     }
26909                     
26910                     if(pos < curr){
26911                         continue;
26912                     }
26913                     
26914                     pos -= lines[i].length;
26915                     
26916                     break;
26917                 }
26918                 
26919                 if(!e.shiftKey){
26920                     this.el.dom.setSelectionRange(pos, pos);
26921                     return;
26922                 }
26923                 
26924                 this.el.dom.selectionStart = pos;
26925                 this.el.dom.selectionEnd = curr;
26926             },
26927             
26928             "end" : function(e){
26929                 e.preventDefault();
26930                 
26931                 var curr = this.el.dom.selectionStart;
26932                 var lines = this.getValue().split("\n");
26933                 
26934                 if(!lines.length){
26935                     return;
26936                 }
26937                 
26938                 if(e.ctrlKey){
26939                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26940                     return;
26941                 }
26942                 
26943                 var pos = 0;
26944                 
26945                 for (var i = 0; i < lines.length;i++) {
26946                     
26947                     pos += lines[i].length;
26948                     
26949                     if(i != 0){
26950                         pos += 1;
26951                     }
26952                     
26953                     if(pos < curr){
26954                         continue;
26955                     }
26956                     
26957                     break;
26958                 }
26959                 
26960                 if(!e.shiftKey){
26961                     this.el.dom.setSelectionRange(pos, pos);
26962                     return;
26963                 }
26964                 
26965                 this.el.dom.selectionStart = curr;
26966                 this.el.dom.selectionEnd = pos;
26967             },
26968
26969             scope : this,
26970
26971             doRelay : function(foo, bar, hname){
26972                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26973             },
26974
26975             forceKeyDown: true
26976         });
26977         
26978 //        if(this.autosave && this.w){
26979 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26980 //        }
26981     },
26982
26983     // private
26984     onResize : function(w, h)
26985     {
26986         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26987         var ew = false;
26988         var eh = false;
26989         
26990         if(this.el ){
26991             if(typeof w == 'number'){
26992                 var aw = w - this.wrap.getFrameWidth('lr');
26993                 this.el.setWidth(this.adjustWidth('textarea', aw));
26994                 ew = aw;
26995             }
26996             if(typeof h == 'number'){
26997                 var tbh = 0;
26998                 for (var i =0; i < this.toolbars.length;i++) {
26999                     // fixme - ask toolbars for heights?
27000                     tbh += this.toolbars[i].tb.el.getHeight();
27001                     if (this.toolbars[i].footer) {
27002                         tbh += this.toolbars[i].footer.el.getHeight();
27003                     }
27004                 }
27005                 
27006                 
27007                 
27008                 
27009                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27010                 ah -= 5; // knock a few pixes off for look..
27011 //                Roo.log(ah);
27012                 this.el.setHeight(this.adjustWidth('textarea', ah));
27013                 var eh = ah;
27014             }
27015         }
27016         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27017         this.editorcore.onResize(ew,eh);
27018         
27019     },
27020
27021     /**
27022      * Toggles the editor between standard and source edit mode.
27023      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27024      */
27025     toggleSourceEdit : function(sourceEditMode)
27026     {
27027         this.editorcore.toggleSourceEdit(sourceEditMode);
27028         
27029         if(this.editorcore.sourceEditMode){
27030             Roo.log('editor - showing textarea');
27031             
27032 //            Roo.log('in');
27033 //            Roo.log(this.syncValue());
27034             this.editorcore.syncValue();
27035             this.el.removeClass('x-hidden');
27036             this.el.dom.removeAttribute('tabIndex');
27037             this.el.focus();
27038             this.el.dom.scrollTop = 0;
27039             
27040             
27041             for (var i = 0; i < this.toolbars.length; i++) {
27042                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27043                     this.toolbars[i].tb.hide();
27044                     this.toolbars[i].footer.hide();
27045                 }
27046             }
27047             
27048         }else{
27049             Roo.log('editor - hiding textarea');
27050 //            Roo.log('out')
27051 //            Roo.log(this.pushValue()); 
27052             this.editorcore.pushValue();
27053             
27054             this.el.addClass('x-hidden');
27055             this.el.dom.setAttribute('tabIndex', -1);
27056             
27057             for (var i = 0; i < this.toolbars.length; i++) {
27058                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27059                     this.toolbars[i].tb.show();
27060                     this.toolbars[i].footer.show();
27061                 }
27062             }
27063             
27064             //this.deferFocus();
27065         }
27066         
27067         this.setSize(this.wrap.getSize());
27068         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27069         
27070         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27071     },
27072  
27073     // private (for BoxComponent)
27074     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27075
27076     // private (for BoxComponent)
27077     getResizeEl : function(){
27078         return this.wrap;
27079     },
27080
27081     // private (for BoxComponent)
27082     getPositionEl : function(){
27083         return this.wrap;
27084     },
27085
27086     // private
27087     initEvents : function(){
27088         this.originalValue = this.getValue();
27089     },
27090
27091     /**
27092      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27093      * @method
27094      */
27095     markInvalid : Roo.emptyFn,
27096     /**
27097      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27098      * @method
27099      */
27100     clearInvalid : Roo.emptyFn,
27101
27102     setValue : function(v){
27103         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27104         this.editorcore.pushValue();
27105     },
27106
27107     /**
27108      * update the language in the body - really done by core
27109      * @param {String} language - eg. en / ar / zh-CN etc..
27110      */
27111     updateLanguage : function(lang)
27112     {
27113         this.language = lang;
27114         this.editorcore.language = lang;
27115         this.editorcore.updateLanguage();
27116      
27117     },
27118     // private
27119     deferFocus : function(){
27120         this.focus.defer(10, this);
27121     },
27122
27123     // doc'ed in Field
27124     focus : function(){
27125         this.editorcore.focus();
27126         
27127     },
27128       
27129
27130     // private
27131     onDestroy : function(){
27132         
27133         
27134         
27135         if(this.rendered){
27136             
27137             for (var i =0; i < this.toolbars.length;i++) {
27138                 // fixme - ask toolbars for heights?
27139                 this.toolbars[i].onDestroy();
27140             }
27141             
27142             this.wrap.dom.innerHTML = '';
27143             this.wrap.remove();
27144         }
27145     },
27146
27147     // private
27148     onFirstFocus : function(){
27149         //Roo.log("onFirstFocus");
27150         this.editorcore.onFirstFocus();
27151          for (var i =0; i < this.toolbars.length;i++) {
27152             this.toolbars[i].onFirstFocus();
27153         }
27154         
27155     },
27156     
27157     // private
27158     syncValue : function()
27159     {
27160         this.editorcore.syncValue();
27161     },
27162     
27163     pushValue : function()
27164     {
27165         this.editorcore.pushValue();
27166     },
27167     
27168     setStylesheets : function(stylesheets)
27169     {
27170         this.editorcore.setStylesheets(stylesheets);
27171     },
27172     
27173     removeStylesheets : function()
27174     {
27175         this.editorcore.removeStylesheets();
27176     }
27177      
27178     
27179     // hide stuff that is not compatible
27180     /**
27181      * @event blur
27182      * @hide
27183      */
27184     /**
27185      * @event change
27186      * @hide
27187      */
27188     /**
27189      * @event focus
27190      * @hide
27191      */
27192     /**
27193      * @event specialkey
27194      * @hide
27195      */
27196     /**
27197      * @cfg {String} fieldClass @hide
27198      */
27199     /**
27200      * @cfg {String} focusClass @hide
27201      */
27202     /**
27203      * @cfg {String} autoCreate @hide
27204      */
27205     /**
27206      * @cfg {String} inputType @hide
27207      */
27208     /**
27209      * @cfg {String} invalidClass @hide
27210      */
27211     /**
27212      * @cfg {String} invalidText @hide
27213      */
27214     /**
27215      * @cfg {String} msgFx @hide
27216      */
27217     /**
27218      * @cfg {String} validateOnBlur @hide
27219      */
27220 });
27221  
27222     /*
27223  * Based on
27224  * Ext JS Library 1.1.1
27225  * Copyright(c) 2006-2007, Ext JS, LLC.
27226  *  
27227  
27228  */
27229
27230 /**
27231  * @class Roo.form.HtmlEditor.ToolbarStandard
27232  * Basic Toolbar
27233
27234  * Usage:
27235  *
27236  new Roo.form.HtmlEditor({
27237     ....
27238     toolbars : [
27239         new Roo.form.HtmlEditorToolbar1({
27240             disable : { fonts: 1 , format: 1, ..., ... , ...],
27241             btns : [ .... ]
27242         })
27243     }
27244      
27245  * 
27246  * @cfg {Object} disable List of elements to disable..
27247  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27248  * 
27249  * 
27250  * NEEDS Extra CSS? 
27251  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27252  */
27253  
27254 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27255 {
27256     
27257     Roo.apply(this, config);
27258     
27259     // default disabled, based on 'good practice'..
27260     this.disable = this.disable || {};
27261     Roo.applyIf(this.disable, {
27262         fontSize : true,
27263         colors : true,
27264         specialElements : true
27265     });
27266     
27267     
27268     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27269     // dont call parent... till later.
27270 }
27271
27272 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27273     
27274     tb: false,
27275     
27276     rendered: false,
27277     
27278     editor : false,
27279     editorcore : false,
27280     /**
27281      * @cfg {Object} disable  List of toolbar elements to disable
27282          
27283      */
27284     disable : false,
27285     
27286     
27287      /**
27288      * @cfg {String} createLinkText The default text for the create link prompt
27289      */
27290     createLinkText : 'Please enter the URL for the link:',
27291     /**
27292      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27293      */
27294     defaultLinkValue : 'http:/'+'/',
27295    
27296     
27297       /**
27298      * @cfg {Array} fontFamilies An array of available font families
27299      */
27300     fontFamilies : [
27301         'Arial',
27302         'Courier New',
27303         'Tahoma',
27304         'Times New Roman',
27305         'Verdana'
27306     ],
27307     
27308     specialChars : [
27309            "&#169;",
27310           "&#174;",     
27311           "&#8482;",    
27312           "&#163;" ,    
27313          // "&#8212;",    
27314           "&#8230;",    
27315           "&#247;" ,    
27316         //  "&#225;" ,     ?? a acute?
27317            "&#8364;"    , //Euro
27318        //   "&#8220;"    ,
27319         //  "&#8221;"    ,
27320         //  "&#8226;"    ,
27321           "&#176;"  //   , // degrees
27322
27323          // "&#233;"     , // e ecute
27324          // "&#250;"     , // u ecute?
27325     ],
27326     
27327     specialElements : [
27328         {
27329             text: "Insert Table",
27330             xtype: 'MenuItem',
27331             xns : Roo.Menu,
27332             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27333                 
27334         },
27335         {    
27336             text: "Insert Image",
27337             xtype: 'MenuItem',
27338             xns : Roo.Menu,
27339             ihtml : '<img src="about:blank"/>'
27340             
27341         }
27342         
27343          
27344     ],
27345     
27346     
27347     inputElements : [ 
27348             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27349             "input:submit", "input:button", "select", "textarea", "label" ],
27350     formats : [
27351         ["p"] ,  
27352         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27353         ["pre"],[ "code"], 
27354         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27355         ['div'],['span'],
27356         ['sup'],['sub']
27357     ],
27358     
27359     cleanStyles : [
27360         "font-size"
27361     ],
27362      /**
27363      * @cfg {String} defaultFont default font to use.
27364      */
27365     defaultFont: 'tahoma',
27366    
27367     fontSelect : false,
27368     
27369     
27370     formatCombo : false,
27371     
27372     init : function(editor)
27373     {
27374         this.editor = editor;
27375         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27376         var editorcore = this.editorcore;
27377         
27378         var _t = this;
27379         
27380         var fid = editorcore.frameId;
27381         var etb = this;
27382         function btn(id, toggle, handler){
27383             var xid = fid + '-'+ id ;
27384             return {
27385                 id : xid,
27386                 cmd : id,
27387                 cls : 'x-btn-icon x-edit-'+id,
27388                 enableToggle:toggle !== false,
27389                 scope: _t, // was editor...
27390                 handler:handler||_t.relayBtnCmd,
27391                 clickEvent:'mousedown',
27392                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27393                 tabIndex:-1
27394             };
27395         }
27396         
27397         
27398         
27399         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27400         this.tb = tb;
27401          // stop form submits
27402         tb.el.on('click', function(e){
27403             e.preventDefault(); // what does this do?
27404         });
27405
27406         if(!this.disable.font) { // && !Roo.isSafari){
27407             /* why no safari for fonts 
27408             editor.fontSelect = tb.el.createChild({
27409                 tag:'select',
27410                 tabIndex: -1,
27411                 cls:'x-font-select',
27412                 html: this.createFontOptions()
27413             });
27414             
27415             editor.fontSelect.on('change', function(){
27416                 var font = editor.fontSelect.dom.value;
27417                 editor.relayCmd('fontname', font);
27418                 editor.deferFocus();
27419             }, editor);
27420             
27421             tb.add(
27422                 editor.fontSelect.dom,
27423                 '-'
27424             );
27425             */
27426             
27427         };
27428         if(!this.disable.formats){
27429             this.formatCombo = new Roo.form.ComboBox({
27430                 store: new Roo.data.SimpleStore({
27431                     id : 'tag',
27432                     fields: ['tag'],
27433                     data : this.formats // from states.js
27434                 }),
27435                 blockFocus : true,
27436                 name : '',
27437                 //autoCreate : {tag: "div",  size: "20"},
27438                 displayField:'tag',
27439                 typeAhead: false,
27440                 mode: 'local',
27441                 editable : false,
27442                 triggerAction: 'all',
27443                 emptyText:'Add tag',
27444                 selectOnFocus:true,
27445                 width:135,
27446                 listeners : {
27447                     'select': function(c, r, i) {
27448                         editorcore.insertTag(r.get('tag'));
27449                         editor.focus();
27450                     }
27451                 }
27452
27453             });
27454             tb.addField(this.formatCombo);
27455             
27456         }
27457         
27458         if(!this.disable.format){
27459             tb.add(
27460                 btn('bold'),
27461                 btn('italic'),
27462                 btn('underline'),
27463                 btn('strikethrough')
27464             );
27465         };
27466         if(!this.disable.fontSize){
27467             tb.add(
27468                 '-',
27469                 
27470                 
27471                 btn('increasefontsize', false, editorcore.adjustFont),
27472                 btn('decreasefontsize', false, editorcore.adjustFont)
27473             );
27474         };
27475         
27476         
27477         if(!this.disable.colors){
27478             tb.add(
27479                 '-', {
27480                     id:editorcore.frameId +'-forecolor',
27481                     cls:'x-btn-icon x-edit-forecolor',
27482                     clickEvent:'mousedown',
27483                     tooltip: this.buttonTips['forecolor'] || undefined,
27484                     tabIndex:-1,
27485                     menu : new Roo.menu.ColorMenu({
27486                         allowReselect: true,
27487                         focus: Roo.emptyFn,
27488                         value:'000000',
27489                         plain:true,
27490                         selectHandler: function(cp, color){
27491                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27492                             editor.deferFocus();
27493                         },
27494                         scope: editorcore,
27495                         clickEvent:'mousedown'
27496                     })
27497                 }, {
27498                     id:editorcore.frameId +'backcolor',
27499                     cls:'x-btn-icon x-edit-backcolor',
27500                     clickEvent:'mousedown',
27501                     tooltip: this.buttonTips['backcolor'] || undefined,
27502                     tabIndex:-1,
27503                     menu : new Roo.menu.ColorMenu({
27504                         focus: Roo.emptyFn,
27505                         value:'FFFFFF',
27506                         plain:true,
27507                         allowReselect: true,
27508                         selectHandler: function(cp, color){
27509                             if(Roo.isGecko){
27510                                 editorcore.execCmd('useCSS', false);
27511                                 editorcore.execCmd('hilitecolor', color);
27512                                 editorcore.execCmd('useCSS', true);
27513                                 editor.deferFocus();
27514                             }else{
27515                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27516                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27517                                 editor.deferFocus();
27518                             }
27519                         },
27520                         scope:editorcore,
27521                         clickEvent:'mousedown'
27522                     })
27523                 }
27524             );
27525         };
27526         // now add all the items...
27527         
27528
27529         if(!this.disable.alignments){
27530             tb.add(
27531                 '-',
27532                 btn('justifyleft'),
27533                 btn('justifycenter'),
27534                 btn('justifyright')
27535             );
27536         };
27537
27538         //if(!Roo.isSafari){
27539             if(!this.disable.links){
27540                 tb.add(
27541                     '-',
27542                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27543                 );
27544             };
27545
27546             if(!this.disable.lists){
27547                 tb.add(
27548                     '-',
27549                     btn('insertorderedlist'),
27550                     btn('insertunorderedlist')
27551                 );
27552             }
27553             if(!this.disable.sourceEdit){
27554                 tb.add(
27555                     '-',
27556                     btn('sourceedit', true, function(btn){
27557                         this.toggleSourceEdit(btn.pressed);
27558                     })
27559                 );
27560             }
27561         //}
27562         
27563         var smenu = { };
27564         // special menu.. - needs to be tidied up..
27565         if (!this.disable.special) {
27566             smenu = {
27567                 text: "&#169;",
27568                 cls: 'x-edit-none',
27569                 
27570                 menu : {
27571                     items : []
27572                 }
27573             };
27574             for (var i =0; i < this.specialChars.length; i++) {
27575                 smenu.menu.items.push({
27576                     
27577                     html: this.specialChars[i],
27578                     handler: function(a,b) {
27579                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27580                         //editor.insertAtCursor(a.html);
27581                         
27582                     },
27583                     tabIndex:-1
27584                 });
27585             }
27586             
27587             
27588             tb.add(smenu);
27589             
27590             
27591         }
27592         
27593         var cmenu = { };
27594         if (!this.disable.cleanStyles) {
27595             cmenu = {
27596                 cls: 'x-btn-icon x-btn-clear',
27597                 
27598                 menu : {
27599                     items : []
27600                 }
27601             };
27602             for (var i =0; i < this.cleanStyles.length; i++) {
27603                 cmenu.menu.items.push({
27604                     actiontype : this.cleanStyles[i],
27605                     html: 'Remove ' + this.cleanStyles[i],
27606                     handler: function(a,b) {
27607 //                        Roo.log(a);
27608 //                        Roo.log(b);
27609                         var c = Roo.get(editorcore.doc.body);
27610                         c.select('[style]').each(function(s) {
27611                             s.dom.style.removeProperty(a.actiontype);
27612                         });
27613                         editorcore.syncValue();
27614                     },
27615                     tabIndex:-1
27616                 });
27617             }
27618             cmenu.menu.items.push({
27619                 actiontype : 'tablewidths',
27620                 html: 'Remove Table Widths',
27621                 handler: function(a,b) {
27622                     editorcore.cleanTableWidths();
27623                     editorcore.syncValue();
27624                 },
27625                 tabIndex:-1
27626             });
27627             cmenu.menu.items.push({
27628                 actiontype : 'word',
27629                 html: 'Remove MS Word Formating',
27630                 handler: function(a,b) {
27631                     editorcore.cleanWord();
27632                     editorcore.syncValue();
27633                 },
27634                 tabIndex:-1
27635             });
27636             
27637             cmenu.menu.items.push({
27638                 actiontype : 'all',
27639                 html: 'Remove All Styles',
27640                 handler: function(a,b) {
27641                     
27642                     var c = Roo.get(editorcore.doc.body);
27643                     c.select('[style]').each(function(s) {
27644                         s.dom.removeAttribute('style');
27645                     });
27646                     editorcore.syncValue();
27647                 },
27648                 tabIndex:-1
27649             });
27650             
27651             cmenu.menu.items.push({
27652                 actiontype : 'all',
27653                 html: 'Remove All CSS Classes',
27654                 handler: function(a,b) {
27655                     
27656                     var c = Roo.get(editorcore.doc.body);
27657                     c.select('[class]').each(function(s) {
27658                         s.dom.removeAttribute('class');
27659                     });
27660                     editorcore.cleanWord();
27661                     editorcore.syncValue();
27662                 },
27663                 tabIndex:-1
27664             });
27665             
27666              cmenu.menu.items.push({
27667                 actiontype : 'tidy',
27668                 html: 'Tidy HTML Source',
27669                 handler: function(a,b) {
27670                     new Roo.htmleditor.Tidy(editorcore.doc.body);
27671                     editorcore.syncValue();
27672                 },
27673                 tabIndex:-1
27674             });
27675             
27676             
27677             tb.add(cmenu);
27678         }
27679          
27680         if (!this.disable.specialElements) {
27681             var semenu = {
27682                 text: "Other;",
27683                 cls: 'x-edit-none',
27684                 menu : {
27685                     items : []
27686                 }
27687             };
27688             for (var i =0; i < this.specialElements.length; i++) {
27689                 semenu.menu.items.push(
27690                     Roo.apply({ 
27691                         handler: function(a,b) {
27692                             editor.insertAtCursor(this.ihtml);
27693                         }
27694                     }, this.specialElements[i])
27695                 );
27696                     
27697             }
27698             
27699             tb.add(semenu);
27700             
27701             
27702         }
27703          
27704         
27705         if (this.btns) {
27706             for(var i =0; i< this.btns.length;i++) {
27707                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27708                 b.cls =  'x-edit-none';
27709                 
27710                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27711                     b.cls += ' x-init-enable';
27712                 }
27713                 
27714                 b.scope = editorcore;
27715                 tb.add(b);
27716             }
27717         
27718         }
27719         
27720         
27721         
27722         // disable everything...
27723         
27724         this.tb.items.each(function(item){
27725             
27726            if(
27727                 item.id != editorcore.frameId+ '-sourceedit' && 
27728                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27729             ){
27730                 
27731                 item.disable();
27732             }
27733         });
27734         this.rendered = true;
27735         
27736         // the all the btns;
27737         editor.on('editorevent', this.updateToolbar, this);
27738         // other toolbars need to implement this..
27739         //editor.on('editmodechange', this.updateToolbar, this);
27740     },
27741     
27742     
27743     relayBtnCmd : function(btn) {
27744         this.editorcore.relayCmd(btn.cmd);
27745     },
27746     // private used internally
27747     createLink : function(){
27748         //Roo.log("create link?");
27749         var ec = this.editorcore;
27750         Roo.MessageBox.prompt("Add Link URL",this.createLinkText, function(btn, url) {
27751             if (btn != 'ok') {
27752                 return;
27753             }
27754             if(url && url != 'http:/'+'/'){
27755                 ec.relayCmd('createlink', url);
27756             }
27757         });
27758         
27759     },
27760
27761     
27762     /**
27763      * Protected method that will not generally be called directly. It triggers
27764      * a toolbar update by reading the markup state of the current selection in the editor.
27765      */
27766     updateToolbar: function(){
27767
27768         if(!this.editorcore.activated){
27769             this.editor.onFirstFocus();
27770             return;
27771         }
27772
27773         var btns = this.tb.items.map, 
27774             doc = this.editorcore.doc,
27775             frameId = this.editorcore.frameId;
27776
27777         if(!this.disable.font && !Roo.isSafari){
27778             /*
27779             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27780             if(name != this.fontSelect.dom.value){
27781                 this.fontSelect.dom.value = name;
27782             }
27783             */
27784         }
27785         if(!this.disable.format){
27786             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27787             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27788             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27789             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27790         }
27791         if(!this.disable.alignments){
27792             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27793             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27794             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27795         }
27796         if(!Roo.isSafari && !this.disable.lists){
27797             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27798             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27799         }
27800         
27801         var ans = this.editorcore.getAllAncestors();
27802         if (this.formatCombo) {
27803             
27804             
27805             var store = this.formatCombo.store;
27806             this.formatCombo.setValue("");
27807             for (var i =0; i < ans.length;i++) {
27808                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27809                     // select it..
27810                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27811                     break;
27812                 }
27813             }
27814         }
27815         
27816         
27817         
27818         // hides menus... - so this cant be on a menu...
27819         Roo.menu.MenuMgr.hideAll();
27820
27821         //this.editorsyncValue();
27822     },
27823    
27824     
27825     createFontOptions : function(){
27826         var buf = [], fs = this.fontFamilies, ff, lc;
27827         
27828         
27829         
27830         for(var i = 0, len = fs.length; i< len; i++){
27831             ff = fs[i];
27832             lc = ff.toLowerCase();
27833             buf.push(
27834                 '<option value="',lc,'" style="font-family:',ff,';"',
27835                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27836                     ff,
27837                 '</option>'
27838             );
27839         }
27840         return buf.join('');
27841     },
27842     
27843     toggleSourceEdit : function(sourceEditMode){
27844         
27845         Roo.log("toolbar toogle");
27846         if(sourceEditMode === undefined){
27847             sourceEditMode = !this.sourceEditMode;
27848         }
27849         this.sourceEditMode = sourceEditMode === true;
27850         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27851         // just toggle the button?
27852         if(btn.pressed !== this.sourceEditMode){
27853             btn.toggle(this.sourceEditMode);
27854             return;
27855         }
27856         
27857         if(sourceEditMode){
27858             Roo.log("disabling buttons");
27859             this.tb.items.each(function(item){
27860                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27861                     item.disable();
27862                 }
27863             });
27864           
27865         }else{
27866             Roo.log("enabling buttons");
27867             if(this.editorcore.initialized){
27868                 this.tb.items.each(function(item){
27869                     item.enable();
27870                 });
27871                 // initialize 'blocks'
27872                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
27873                     Roo.htmleditor.Block.factory(e).updateElement(e);
27874                 },this);
27875             
27876             }
27877             
27878         }
27879         Roo.log("calling toggole on editor");
27880         // tell the editor that it's been pressed..
27881         this.editor.toggleSourceEdit(sourceEditMode);
27882        
27883     },
27884      /**
27885      * Object collection of toolbar tooltips for the buttons in the editor. The key
27886      * is the command id associated with that button and the value is a valid QuickTips object.
27887      * For example:
27888 <pre><code>
27889 {
27890     bold : {
27891         title: 'Bold (Ctrl+B)',
27892         text: 'Make the selected text bold.',
27893         cls: 'x-html-editor-tip'
27894     },
27895     italic : {
27896         title: 'Italic (Ctrl+I)',
27897         text: 'Make the selected text italic.',
27898         cls: 'x-html-editor-tip'
27899     },
27900     ...
27901 </code></pre>
27902     * @type Object
27903      */
27904     buttonTips : {
27905         bold : {
27906             title: 'Bold (Ctrl+B)',
27907             text: 'Make the selected text bold.',
27908             cls: 'x-html-editor-tip'
27909         },
27910         italic : {
27911             title: 'Italic (Ctrl+I)',
27912             text: 'Make the selected text italic.',
27913             cls: 'x-html-editor-tip'
27914         },
27915         underline : {
27916             title: 'Underline (Ctrl+U)',
27917             text: 'Underline the selected text.',
27918             cls: 'x-html-editor-tip'
27919         },
27920         strikethrough : {
27921             title: 'Strikethrough',
27922             text: 'Strikethrough the selected text.',
27923             cls: 'x-html-editor-tip'
27924         },
27925         increasefontsize : {
27926             title: 'Grow Text',
27927             text: 'Increase the font size.',
27928             cls: 'x-html-editor-tip'
27929         },
27930         decreasefontsize : {
27931             title: 'Shrink Text',
27932             text: 'Decrease the font size.',
27933             cls: 'x-html-editor-tip'
27934         },
27935         backcolor : {
27936             title: 'Text Highlight Color',
27937             text: 'Change the background color of the selected text.',
27938             cls: 'x-html-editor-tip'
27939         },
27940         forecolor : {
27941             title: 'Font Color',
27942             text: 'Change the color of the selected text.',
27943             cls: 'x-html-editor-tip'
27944         },
27945         justifyleft : {
27946             title: 'Align Text Left',
27947             text: 'Align text to the left.',
27948             cls: 'x-html-editor-tip'
27949         },
27950         justifycenter : {
27951             title: 'Center Text',
27952             text: 'Center text in the editor.',
27953             cls: 'x-html-editor-tip'
27954         },
27955         justifyright : {
27956             title: 'Align Text Right',
27957             text: 'Align text to the right.',
27958             cls: 'x-html-editor-tip'
27959         },
27960         insertunorderedlist : {
27961             title: 'Bullet List',
27962             text: 'Start a bulleted list.',
27963             cls: 'x-html-editor-tip'
27964         },
27965         insertorderedlist : {
27966             title: 'Numbered List',
27967             text: 'Start a numbered list.',
27968             cls: 'x-html-editor-tip'
27969         },
27970         createlink : {
27971             title: 'Hyperlink',
27972             text: 'Make the selected text a hyperlink.',
27973             cls: 'x-html-editor-tip'
27974         },
27975         sourceedit : {
27976             title: 'Source Edit',
27977             text: 'Switch to source editing mode.',
27978             cls: 'x-html-editor-tip'
27979         }
27980     },
27981     // private
27982     onDestroy : function(){
27983         if(this.rendered){
27984             
27985             this.tb.items.each(function(item){
27986                 if(item.menu){
27987                     item.menu.removeAll();
27988                     if(item.menu.el){
27989                         item.menu.el.destroy();
27990                     }
27991                 }
27992                 item.destroy();
27993             });
27994              
27995         }
27996     },
27997     onFirstFocus: function() {
27998         this.tb.items.each(function(item){
27999            item.enable();
28000         });
28001     }
28002 };
28003
28004
28005
28006
28007 // <script type="text/javascript">
28008 /*
28009  * Based on
28010  * Ext JS Library 1.1.1
28011  * Copyright(c) 2006-2007, Ext JS, LLC.
28012  *  
28013  
28014  */
28015
28016  
28017 /**
28018  * @class Roo.form.HtmlEditor.ToolbarContext
28019  * Context Toolbar
28020  * 
28021  * Usage:
28022  *
28023  new Roo.form.HtmlEditor({
28024     ....
28025     toolbars : [
28026         { xtype: 'ToolbarStandard', styles : {} }
28027         { xtype: 'ToolbarContext', disable : {} }
28028     ]
28029 })
28030
28031      
28032  * 
28033  * @config : {Object} disable List of elements to disable.. (not done yet.)
28034  * @config : {Object} styles  Map of styles available.
28035  * 
28036  */
28037
28038 Roo.form.HtmlEditor.ToolbarContext = function(config)
28039 {
28040     
28041     Roo.apply(this, config);
28042     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28043     // dont call parent... till later.
28044     this.styles = this.styles || {};
28045 }
28046
28047  
28048
28049 Roo.form.HtmlEditor.ToolbarContext.types = {
28050     'IMG' : [
28051         {
28052             name : 'width',
28053             title: "Width",
28054             width: 40
28055         },
28056         {
28057             name : 'height',
28058             title: "Height",
28059             width: 40
28060         },
28061         {
28062             name : 'align',
28063             title: "Align",
28064             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28065             width : 80
28066             
28067         },
28068         {
28069             name : 'border',
28070             title: "Border",
28071             width: 40
28072         },
28073         {
28074             name : 'alt',
28075             title: "Alt",
28076             width: 120
28077         },
28078         {
28079             name : 'src',
28080             title: "Src",
28081             width: 220
28082         }
28083         
28084     ],
28085     
28086     'FIGURE' : [
28087         {
28088             name : 'align',
28089             title: "Align",
28090             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28091             width : 80  
28092         }
28093     ],
28094     'A' : [
28095         {
28096             name : 'name',
28097             title: "Name",
28098             width: 50
28099         },
28100         {
28101             name : 'target',
28102             title: "Target",
28103             width: 120
28104         },
28105         {
28106             name : 'href',
28107             title: "Href",
28108             width: 220
28109         } // border?
28110         
28111     ],
28112     
28113     'INPUT' : [
28114         {
28115             name : 'name',
28116             title: "name",
28117             width: 120
28118         },
28119         {
28120             name : 'value',
28121             title: "Value",
28122             width: 120
28123         },
28124         {
28125             name : 'width',
28126             title: "Width",
28127             width: 40
28128         }
28129     ],
28130     'LABEL' : [
28131          {
28132             name : 'for',
28133             title: "For",
28134             width: 120
28135         }
28136     ],
28137     'TEXTAREA' : [
28138         {
28139             name : 'name',
28140             title: "name",
28141             width: 120
28142         },
28143         {
28144             name : 'rows',
28145             title: "Rows",
28146             width: 20
28147         },
28148         {
28149             name : 'cols',
28150             title: "Cols",
28151             width: 20
28152         }
28153     ],
28154     'SELECT' : [
28155         {
28156             name : 'name',
28157             title: "name",
28158             width: 120
28159         },
28160         {
28161             name : 'selectoptions',
28162             title: "Options",
28163             width: 200
28164         }
28165     ],
28166     
28167     // should we really allow this??
28168     // should this just be 
28169     'BODY' : [
28170         
28171         {
28172             name : 'title',
28173             title: "Title",
28174             width: 200,
28175             disabled : true
28176         }
28177     ],
28178  
28179     '*' : [
28180         // empty.
28181     ]
28182
28183 };
28184
28185 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28186 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28187
28188 Roo.form.HtmlEditor.ToolbarContext.options = {
28189         'font-family'  : [ 
28190                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28191                 [ 'Courier New', 'Courier New'],
28192                 [ 'Tahoma', 'Tahoma'],
28193                 [ 'Times New Roman,serif', 'Times'],
28194                 [ 'Verdana','Verdana' ]
28195         ]
28196 };
28197
28198 // fixme - these need to be configurable..
28199  
28200
28201 //Roo.form.HtmlEditor.ToolbarContext.types
28202
28203
28204 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28205     
28206     tb: false,
28207     
28208     rendered: false,
28209     
28210     editor : false,
28211     editorcore : false,
28212     /**
28213      * @cfg {Object} disable  List of toolbar elements to disable
28214          
28215      */
28216     disable : false,
28217     /**
28218      * @cfg {Object} styles List of styles 
28219      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28220      *
28221      * These must be defined in the page, so they get rendered correctly..
28222      * .headline { }
28223      * TD.underline { }
28224      * 
28225      */
28226     styles : false,
28227     
28228     options: false,
28229     
28230     toolbars : false,
28231     
28232     init : function(editor)
28233     {
28234         this.editor = editor;
28235         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28236         var editorcore = this.editorcore;
28237         
28238         var fid = editorcore.frameId;
28239         var etb = this;
28240         function btn(id, toggle, handler){
28241             var xid = fid + '-'+ id ;
28242             return {
28243                 id : xid,
28244                 cmd : id,
28245                 cls : 'x-btn-icon x-edit-'+id,
28246                 enableToggle:toggle !== false,
28247                 scope: editorcore, // was editor...
28248                 handler:handler||editorcore.relayBtnCmd,
28249                 clickEvent:'mousedown',
28250                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28251                 tabIndex:-1
28252             };
28253         }
28254         // create a new element.
28255         var wdiv = editor.wrap.createChild({
28256                 tag: 'div'
28257             }, editor.wrap.dom.firstChild.nextSibling, true);
28258         
28259         // can we do this more than once??
28260         
28261          // stop form submits
28262       
28263  
28264         // disable everything...
28265         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28266         this.toolbars = {};
28267         // block toolbars are built in updateToolbar when needed.
28268         for (var i in  ty) {
28269             
28270             this.toolbars[i] = this.buildToolbar(ty[i],i);
28271         }
28272         this.tb = this.toolbars.BODY;
28273         this.tb.el.show();
28274         this.buildFooter();
28275         this.footer.show();
28276         editor.on('hide', function( ) { this.footer.hide() }, this);
28277         editor.on('show', function( ) { this.footer.show() }, this);
28278         
28279          
28280         this.rendered = true;
28281         
28282         // the all the btns;
28283         editor.on('editorevent', this.updateToolbar, this);
28284         // other toolbars need to implement this..
28285         //editor.on('editmodechange', this.updateToolbar, this);
28286     },
28287     
28288     
28289     
28290     /**
28291      * Protected method that will not generally be called directly. It triggers
28292      * a toolbar update by reading the markup state of the current selection in the editor.
28293      *
28294      * Note you can force an update by calling on('editorevent', scope, false)
28295      */
28296     updateToolbar: function(editor ,ev, sel)
28297     {
28298         
28299         if (ev) {
28300             ev.stopEvent(); // se if we can stop this looping with mutiple events.
28301         }
28302         
28303         //Roo.log(ev);
28304         // capture mouse up - this is handy for selecting images..
28305         // perhaps should go somewhere else...
28306         if(!this.editorcore.activated){
28307              this.editor.onFirstFocus();
28308             return;
28309         }
28310         //Roo.log(ev ? ev.target : 'NOTARGET');
28311         
28312         
28313         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28314         // selectNode - might want to handle IE?
28315         
28316         
28317         
28318         if (ev &&
28319             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28320             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28321             // they have click on an image...
28322             // let's see if we can change the selection...
28323             sel = ev.target;
28324             
28325             // this triggers looping?
28326             //this.editorcore.selectNode(sel);
28327              
28328         }
28329         
28330         // this forces an id..
28331         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28332              e.classList.remove('roo-ed-selection');
28333         });
28334         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28335         //Roo.get(node).addClass('roo-ed-selection');
28336       
28337         //var updateFooter = sel ? false : true; 
28338         
28339         
28340         var ans = this.editorcore.getAllAncestors();
28341         
28342         // pick
28343         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28344         
28345         if (!sel) { 
28346             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28347             sel = sel ? sel : this.editorcore.doc.body;
28348             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28349             
28350         }
28351         
28352         var tn = sel.tagName.toUpperCase();
28353         var lastSel = this.tb.selectedNode;
28354         this.tb.selectedNode = sel;
28355         var left_label = tn;
28356         
28357         // ok see if we are editing a block?
28358         
28359         var db = false;
28360         // you are not actually selecting the block.
28361         if (sel && sel.hasAttribute('data-block')) {
28362             db = sel;
28363         } else if (sel && sel.closest('[data-block]')) {
28364             
28365             db = sel.closest('[data-block]');
28366             //var cepar = sel.closest('[contenteditable=true]');
28367             //if (db && cepar && cepar.tagName != 'BODY') {
28368             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28369             //}   
28370         }
28371         
28372         
28373         var block = false;
28374         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28375         if (db && this.editorcore.enableBlocks) {
28376             block = Roo.htmleditor.Block.factory(db);
28377             
28378             
28379             if (block) {
28380                  db.className = (
28381                         db.classList.length > 0  ? db.className + ' ' : ''
28382                     )  + 'roo-ed-selection';
28383                  
28384                  // since we removed it earlier... its not there..
28385                 tn = 'BLOCK.' + db.getAttribute('data-block');
28386                 
28387                 //this.editorcore.selectNode(db);
28388                 if (typeof(this.toolbars[tn]) == 'undefined') {
28389                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
28390                 }
28391                 this.toolbars[tn].selectedNode = db;
28392                 left_label = block.friendly_name;
28393                 ans = this.editorcore.getAllAncestors();
28394             }
28395             
28396                 
28397             
28398         }
28399         
28400         
28401         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28402             return; // no change?
28403         }
28404         
28405         
28406           
28407         this.tb.el.hide();
28408         ///console.log("show: " + tn);
28409         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28410         
28411         this.tb.el.show();
28412         // update name
28413         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
28414         
28415         
28416         // update attributes
28417         if (block && this.tb.fields) {
28418              
28419             this.tb.fields.each(function(e) {
28420                 e.setValue(block[e.name]);
28421             });
28422             
28423             
28424         } else  if (this.tb.fields && this.tb.selectedNode) {
28425             this.tb.fields.each( function(e) {
28426                 if (e.stylename) {
28427                     e.setValue(this.tb.selectedNode.style[e.stylename]);
28428                     return;
28429                 } 
28430                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28431             }, this);
28432             this.updateToolbarStyles(this.tb.selectedNode);  
28433         }
28434         
28435         
28436        
28437         Roo.menu.MenuMgr.hideAll();
28438
28439         
28440         
28441     
28442         // update the footer
28443         //
28444         this.updateFooter(ans);
28445              
28446     },
28447     
28448     updateToolbarStyles : function(sel)
28449     {
28450         var hasStyles = false;
28451         for(var i in this.styles) {
28452             hasStyles = true;
28453             break;
28454         }
28455         
28456         // update styles
28457         if (hasStyles && this.tb.hasStyles) { 
28458             var st = this.tb.fields.item(0);
28459             
28460             st.store.removeAll();
28461             var cn = sel.className.split(/\s+/);
28462             
28463             var avs = [];
28464             if (this.styles['*']) {
28465                 
28466                 Roo.each(this.styles['*'], function(v) {
28467                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28468                 });
28469             }
28470             if (this.styles[tn]) { 
28471                 Roo.each(this.styles[tn], function(v) {
28472                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28473                 });
28474             }
28475             
28476             st.store.loadData(avs);
28477             st.collapse();
28478             st.setValue(cn);
28479         }
28480     },
28481     
28482      
28483     updateFooter : function(ans)
28484     {
28485         var html = '';
28486         if (ans === false) {
28487             this.footDisp.dom.innerHTML = '';
28488             return;
28489         }
28490         
28491         this.footerEls = ans.reverse();
28492         Roo.each(this.footerEls, function(a,i) {
28493             if (!a) { return; }
28494             html += html.length ? ' &gt; '  :  '';
28495             
28496             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28497             
28498         });
28499        
28500         // 
28501         var sz = this.footDisp.up('td').getSize();
28502         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28503         this.footDisp.dom.style.marginLeft = '5px';
28504         
28505         this.footDisp.dom.style.overflow = 'hidden';
28506         
28507         this.footDisp.dom.innerHTML = html;
28508             
28509         
28510     },
28511    
28512        
28513     // private
28514     onDestroy : function(){
28515         if(this.rendered){
28516             
28517             this.tb.items.each(function(item){
28518                 if(item.menu){
28519                     item.menu.removeAll();
28520                     if(item.menu.el){
28521                         item.menu.el.destroy();
28522                     }
28523                 }
28524                 item.destroy();
28525             });
28526              
28527         }
28528     },
28529     onFirstFocus: function() {
28530         // need to do this for all the toolbars..
28531         this.tb.items.each(function(item){
28532            item.enable();
28533         });
28534     },
28535     buildToolbar: function(tlist, nm, friendly_name, block)
28536     {
28537         var editor = this.editor;
28538         var editorcore = this.editorcore;
28539          // create a new element.
28540         var wdiv = editor.wrap.createChild({
28541                 tag: 'div'
28542             }, editor.wrap.dom.firstChild.nextSibling, true);
28543         
28544        
28545         var tb = new Roo.Toolbar(wdiv);
28546         ///this.tb = tb; // << this sets the active toolbar..
28547         if (tlist === false && block) {
28548             tlist = block.contextMenu(this);
28549         }
28550         
28551         tb.hasStyles = false;
28552         tb.name = nm;
28553         
28554         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
28555         
28556         var styles = Array.from(this.styles);
28557         
28558         
28559         // styles...
28560         if (styles && styles.length) {
28561             tb.hasStyles = true;
28562             // this needs a multi-select checkbox...
28563             tb.addField( new Roo.form.ComboBox({
28564                 store: new Roo.data.SimpleStore({
28565                     id : 'val',
28566                     fields: ['val', 'selected'],
28567                     data : [] 
28568                 }),
28569                 name : '-roo-edit-className',
28570                 attrname : 'className',
28571                 displayField: 'val',
28572                 typeAhead: false,
28573                 mode: 'local',
28574                 editable : false,
28575                 triggerAction: 'all',
28576                 emptyText:'Select Style',
28577                 selectOnFocus:true,
28578                 width: 130,
28579                 listeners : {
28580                     'select': function(c, r, i) {
28581                         // initial support only for on class per el..
28582                         tb.selectedNode.className =  r ? r.get('val') : '';
28583                         editorcore.syncValue();
28584                     }
28585                 }
28586     
28587             }));
28588         }
28589         
28590         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28591         
28592         
28593         for (var i = 0; i < tlist.length; i++) {
28594             
28595             // newer versions will use xtype cfg to create menus.
28596             if (typeof(tlist[i].xtype) != 'undefined') {
28597                 
28598                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28599                 
28600                 
28601                 continue;
28602             }
28603             
28604             var item = tlist[i];
28605             tb.add(item.title + ":&nbsp;");
28606             
28607             
28608             //optname == used so you can configure the options available..
28609             var opts = item.opts ? item.opts : false;
28610             if (item.optname) { // use the b
28611                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28612            
28613             }
28614             
28615             if (opts) {
28616                 // opts == pulldown..
28617                 tb.addField( new Roo.form.ComboBox({
28618                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28619                         id : 'val',
28620                         fields: ['val', 'display'],
28621                         data : opts  
28622                     }),
28623                     name : '-roo-edit-' + tlist[i].name,
28624                     
28625                     attrname : tlist[i].name,
28626                     stylename : item.style ? item.style : false,
28627                     
28628                     displayField: item.displayField ? item.displayField : 'val',
28629                     valueField :  'val',
28630                     typeAhead: false,
28631                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
28632                     editable : false,
28633                     triggerAction: 'all',
28634                     emptyText:'Select',
28635                     selectOnFocus:true,
28636                     width: item.width ? item.width  : 130,
28637                     listeners : {
28638                         'select': function(c, r, i) {
28639                              
28640                             
28641                             if (c.stylename) {
28642                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28643                                 editorcore.syncValue();
28644                                 return;
28645                             }
28646                             if (r === false) {
28647                                 tb.selectedNode.removeAttribute(c.attrname);
28648                                 editorcore.syncValue();
28649                                 return;
28650                             }
28651                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28652                             editorcore.syncValue();
28653                         }
28654                     }
28655
28656                 }));
28657                 continue;
28658                     
28659                  
28660                 /*
28661                 tb.addField( new Roo.form.TextField({
28662                     name: i,
28663                     width: 100,
28664                     //allowBlank:false,
28665                     value: ''
28666                 }));
28667                 continue;
28668                 */
28669             }
28670             tb.addField( new Roo.form.TextField({
28671                 name: '-roo-edit-' + tlist[i].name,
28672                 attrname : tlist[i].name,
28673                 
28674                 width: item.width,
28675                 //allowBlank:true,
28676                 value: '',
28677                 listeners: {
28678                     'change' : function(f, nv, ov) {
28679                         
28680                          
28681                         tb.selectedNode.setAttribute(f.attrname, nv);
28682                         editorcore.syncValue();
28683                     }
28684                 }
28685             }));
28686              
28687         }
28688         
28689         var _this = this;
28690         var show_delete = !block || block.deleteTitle !== false;
28691         if(nm == 'BODY'){
28692             show_delete = false;
28693             tb.addSeparator();
28694         
28695             tb.addButton( {
28696                 text: 'Stylesheets',
28697
28698                 listeners : {
28699                     click : function ()
28700                     {
28701                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28702                     }
28703                 }
28704             });
28705         }
28706         
28707         tb.addFill();
28708         if (show_delete) {
28709             tb.addButton({
28710                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28711         
28712                 listeners : {
28713                     click : function ()
28714                     {
28715                         var sn = tb.selectedNode;
28716                         if (block) {
28717                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28718                             
28719                         }
28720                         if (!sn) {
28721                             return;
28722                         }
28723                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
28724                         if (sn.hasAttribute('data-block')) {
28725                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
28726                             sn.parentNode.removeChild(sn);
28727                             
28728                         } else if (sn && sn.tagName != 'BODY') {
28729                             // remove and keep parents.
28730                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
28731                             a.replaceTag(sn);
28732                         }
28733                         
28734                         
28735                         var range = editorcore.createRange();
28736             
28737                         range.setStart(stn,0);
28738                         range.setEnd(stn,0); 
28739                         var selection = editorcore.getSelection();
28740                         selection.removeAllRanges();
28741                         selection.addRange(range);
28742                         
28743                         
28744                         //_this.updateToolbar(null, null, pn);
28745                         _this.updateToolbar(null, null, null);
28746                         _this.updateFooter(false);
28747                         
28748                     }
28749                 }
28750                 
28751                         
28752                     
28753                 
28754             });
28755         }    
28756         
28757         tb.el.on('click', function(e){
28758             e.preventDefault(); // what does this do?
28759         });
28760         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28761         tb.el.hide();
28762         
28763         // dont need to disable them... as they will get hidden
28764         return tb;
28765          
28766         
28767     },
28768     buildFooter : function()
28769     {
28770         
28771         var fel = this.editor.wrap.createChild();
28772         this.footer = new Roo.Toolbar(fel);
28773         // toolbar has scrolly on left / right?
28774         var footDisp= new Roo.Toolbar.Fill();
28775         var _t = this;
28776         this.footer.add(
28777             {
28778                 text : '&lt;',
28779                 xtype: 'Button',
28780                 handler : function() {
28781                     _t.footDisp.scrollTo('left',0,true)
28782                 }
28783             }
28784         );
28785         this.footer.add( footDisp );
28786         this.footer.add( 
28787             {
28788                 text : '&gt;',
28789                 xtype: 'Button',
28790                 handler : function() {
28791                     // no animation..
28792                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28793                 }
28794             }
28795         );
28796         var fel = Roo.get(footDisp.el);
28797         fel.addClass('x-editor-context');
28798         this.footDispWrap = fel; 
28799         this.footDispWrap.overflow  = 'hidden';
28800         
28801         this.footDisp = fel.createChild();
28802         this.footDispWrap.on('click', this.onContextClick, this)
28803         
28804         
28805     },
28806     // when the footer contect changes
28807     onContextClick : function (ev,dom)
28808     {
28809         ev.preventDefault();
28810         var  cn = dom.className;
28811         //Roo.log(cn);
28812         if (!cn.match(/x-ed-loc-/)) {
28813             return;
28814         }
28815         var n = cn.split('-').pop();
28816         var ans = this.footerEls;
28817         var sel = ans[n];
28818         
28819         this.editorcore.selectNode(sel);
28820         
28821         
28822         this.updateToolbar(null, null, sel);
28823         
28824         
28825     }
28826     
28827     
28828     
28829     
28830     
28831 });
28832
28833
28834
28835
28836
28837 /*
28838  * Based on:
28839  * Ext JS Library 1.1.1
28840  * Copyright(c) 2006-2007, Ext JS, LLC.
28841  *
28842  * Originally Released Under LGPL - original licence link has changed is not relivant.
28843  *
28844  * Fork - LGPL
28845  * <script type="text/javascript">
28846  */
28847  
28848 /**
28849  * @class Roo.form.BasicForm
28850  * @extends Roo.util.Observable
28851  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28852  * @constructor
28853  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28854  * @param {Object} config Configuration options
28855  */
28856 Roo.form.BasicForm = function(el, config){
28857     this.allItems = [];
28858     this.childForms = [];
28859     Roo.apply(this, config);
28860     /*
28861      * The Roo.form.Field items in this form.
28862      * @type MixedCollection
28863      */
28864      
28865      
28866     this.items = new Roo.util.MixedCollection(false, function(o){
28867         return o.id || (o.id = Roo.id());
28868     });
28869     this.addEvents({
28870         /**
28871          * @event beforeaction
28872          * Fires before any action is performed. Return false to cancel the action.
28873          * @param {Form} this
28874          * @param {Action} action The action to be performed
28875          */
28876         beforeaction: true,
28877         /**
28878          * @event actionfailed
28879          * Fires when an action fails.
28880          * @param {Form} this
28881          * @param {Action} action The action that failed
28882          */
28883         actionfailed : true,
28884         /**
28885          * @event actioncomplete
28886          * Fires when an action is completed.
28887          * @param {Form} this
28888          * @param {Action} action The action that completed
28889          */
28890         actioncomplete : true
28891     });
28892     if(el){
28893         this.initEl(el);
28894     }
28895     Roo.form.BasicForm.superclass.constructor.call(this);
28896     
28897     Roo.form.BasicForm.popover.apply();
28898 };
28899
28900 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28901     /**
28902      * @cfg {String} method
28903      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28904      */
28905     /**
28906      * @cfg {DataReader} reader
28907      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28908      * This is optional as there is built-in support for processing JSON.
28909      */
28910     /**
28911      * @cfg {DataReader} errorReader
28912      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28913      * This is completely optional as there is built-in support for processing JSON.
28914      */
28915     /**
28916      * @cfg {String} url
28917      * The URL to use for form actions if one isn't supplied in the action options.
28918      */
28919     /**
28920      * @cfg {Boolean} fileUpload
28921      * Set to true if this form is a file upload.
28922      */
28923      
28924     /**
28925      * @cfg {Object} baseParams
28926      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28927      */
28928      /**
28929      
28930     /**
28931      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28932      */
28933     timeout: 30,
28934
28935     // private
28936     activeAction : null,
28937
28938     /**
28939      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28940      * or setValues() data instead of when the form was first created.
28941      */
28942     trackResetOnLoad : false,
28943     
28944     
28945     /**
28946      * childForms - used for multi-tab forms
28947      * @type {Array}
28948      */
28949     childForms : false,
28950     
28951     /**
28952      * allItems - full list of fields.
28953      * @type {Array}
28954      */
28955     allItems : false,
28956     
28957     /**
28958      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28959      * element by passing it or its id or mask the form itself by passing in true.
28960      * @type Mixed
28961      */
28962     waitMsgTarget : false,
28963     
28964     /**
28965      * @type Boolean
28966      */
28967     disableMask : false,
28968     
28969     /**
28970      * @cfg {Boolean} errorMask (true|false) default false
28971      */
28972     errorMask : false,
28973     
28974     /**
28975      * @cfg {Number} maskOffset Default 100
28976      */
28977     maskOffset : 100,
28978
28979     // private
28980     initEl : function(el){
28981         this.el = Roo.get(el);
28982         this.id = this.el.id || Roo.id();
28983         this.el.on('submit', this.onSubmit, this);
28984         this.el.addClass('x-form');
28985     },
28986
28987     // private
28988     onSubmit : function(e){
28989         e.stopEvent();
28990     },
28991
28992     /**
28993      * Returns true if client-side validation on the form is successful.
28994      * @return Boolean
28995      */
28996     isValid : function(){
28997         var valid = true;
28998         var target = false;
28999         this.items.each(function(f){
29000             if(f.validate()){
29001                 return;
29002             }
29003             
29004             valid = false;
29005                 
29006             if(!target && f.el.isVisible(true)){
29007                 target = f;
29008             }
29009         });
29010         
29011         if(this.errorMask && !valid){
29012             Roo.form.BasicForm.popover.mask(this, target);
29013         }
29014         
29015         return valid;
29016     },
29017     /**
29018      * Returns array of invalid form fields.
29019      * @return Array
29020      */
29021     
29022     invalidFields : function()
29023     {
29024         var ret = [];
29025         this.items.each(function(f){
29026             if(f.validate()){
29027                 return;
29028             }
29029             ret.push(f);
29030             
29031         });
29032         
29033         return ret;
29034     },
29035     
29036     
29037     /**
29038      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
29039      * @return Boolean
29040      */
29041     isDirty : function(){
29042         var dirty = false;
29043         this.items.each(function(f){
29044            if(f.isDirty()){
29045                dirty = true;
29046                return false;
29047            }
29048         });
29049         return dirty;
29050     },
29051     
29052     /**
29053      * Returns true if any fields in this form have changed since their original load. (New version)
29054      * @return Boolean
29055      */
29056     
29057     hasChanged : function()
29058     {
29059         var dirty = false;
29060         this.items.each(function(f){
29061            if(f.hasChanged()){
29062                dirty = true;
29063                return false;
29064            }
29065         });
29066         return dirty;
29067         
29068     },
29069     /**
29070      * Resets all hasChanged to 'false' -
29071      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29072      * So hasChanged storage is only to be used for this purpose
29073      * @return Boolean
29074      */
29075     resetHasChanged : function()
29076     {
29077         this.items.each(function(f){
29078            f.resetHasChanged();
29079         });
29080         
29081     },
29082     
29083     
29084     /**
29085      * Performs a predefined action (submit or load) or custom actions you define on this form.
29086      * @param {String} actionName The name of the action type
29087      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
29088      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29089      * accept other config options):
29090      * <pre>
29091 Property          Type             Description
29092 ----------------  ---------------  ----------------------------------------------------------------------------------
29093 url               String           The url for the action (defaults to the form's url)
29094 method            String           The form method to use (defaults to the form's method, or POST if not defined)
29095 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
29096 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
29097                                    validate the form on the client (defaults to false)
29098      * </pre>
29099      * @return {BasicForm} this
29100      */
29101     doAction : function(action, options){
29102         if(typeof action == 'string'){
29103             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29104         }
29105         if(this.fireEvent('beforeaction', this, action) !== false){
29106             this.beforeAction(action);
29107             action.run.defer(100, action);
29108         }
29109         return this;
29110     },
29111
29112     /**
29113      * Shortcut to do a submit action.
29114      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29115      * @return {BasicForm} this
29116      */
29117     submit : function(options){
29118         this.doAction('submit', options);
29119         return this;
29120     },
29121
29122     /**
29123      * Shortcut to do a load action.
29124      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29125      * @return {BasicForm} this
29126      */
29127     load : function(options){
29128         this.doAction('load', options);
29129         return this;
29130     },
29131
29132     /**
29133      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29134      * @param {Record} record The record to edit
29135      * @return {BasicForm} this
29136      */
29137     updateRecord : function(record){
29138         record.beginEdit();
29139         var fs = record.fields;
29140         fs.each(function(f){
29141             var field = this.findField(f.name);
29142             if(field){
29143                 record.set(f.name, field.getValue());
29144             }
29145         }, this);
29146         record.endEdit();
29147         return this;
29148     },
29149
29150     /**
29151      * Loads an Roo.data.Record into this form.
29152      * @param {Record} record The record to load
29153      * @return {BasicForm} this
29154      */
29155     loadRecord : function(record){
29156         this.setValues(record.data);
29157         return this;
29158     },
29159
29160     // private
29161     beforeAction : function(action){
29162         var o = action.options;
29163         
29164         if(!this.disableMask) {
29165             if(this.waitMsgTarget === true){
29166                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29167             }else if(this.waitMsgTarget){
29168                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29169                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29170             }else {
29171                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29172             }
29173         }
29174         
29175          
29176     },
29177
29178     // private
29179     afterAction : function(action, success){
29180         this.activeAction = null;
29181         var o = action.options;
29182         
29183         if(!this.disableMask) {
29184             if(this.waitMsgTarget === true){
29185                 this.el.unmask();
29186             }else if(this.waitMsgTarget){
29187                 this.waitMsgTarget.unmask();
29188             }else{
29189                 Roo.MessageBox.updateProgress(1);
29190                 Roo.MessageBox.hide();
29191             }
29192         }
29193         
29194         if(success){
29195             if(o.reset){
29196                 this.reset();
29197             }
29198             Roo.callback(o.success, o.scope, [this, action]);
29199             this.fireEvent('actioncomplete', this, action);
29200             
29201         }else{
29202             
29203             // failure condition..
29204             // we have a scenario where updates need confirming.
29205             // eg. if a locking scenario exists..
29206             // we look for { errors : { needs_confirm : true }} in the response.
29207             if (
29208                 (typeof(action.result) != 'undefined')  &&
29209                 (typeof(action.result.errors) != 'undefined')  &&
29210                 (typeof(action.result.errors.needs_confirm) != 'undefined')
29211            ){
29212                 var _t = this;
29213                 Roo.MessageBox.confirm(
29214                     "Change requires confirmation",
29215                     action.result.errorMsg,
29216                     function(r) {
29217                         if (r != 'yes') {
29218                             return;
29219                         }
29220                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
29221                     }
29222                     
29223                 );
29224                 
29225                 
29226                 
29227                 return;
29228             }
29229             
29230             Roo.callback(o.failure, o.scope, [this, action]);
29231             // show an error message if no failed handler is set..
29232             if (!this.hasListener('actionfailed')) {
29233                 Roo.MessageBox.alert("Error",
29234                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29235                         action.result.errorMsg :
29236                         "Saving Failed, please check your entries or try again"
29237                 );
29238             }
29239             
29240             this.fireEvent('actionfailed', this, action);
29241         }
29242         
29243     },
29244
29245     /**
29246      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29247      * @param {String} id The value to search for
29248      * @return Field
29249      */
29250     findField : function(id){
29251         var field = this.items.get(id);
29252         if(!field){
29253             this.items.each(function(f){
29254                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29255                     field = f;
29256                     return false;
29257                 }
29258             });
29259         }
29260         return field || null;
29261     },
29262
29263     /**
29264      * Add a secondary form to this one, 
29265      * Used to provide tabbed forms. One form is primary, with hidden values 
29266      * which mirror the elements from the other forms.
29267      * 
29268      * @param {Roo.form.Form} form to add.
29269      * 
29270      */
29271     addForm : function(form)
29272     {
29273        
29274         if (this.childForms.indexOf(form) > -1) {
29275             // already added..
29276             return;
29277         }
29278         this.childForms.push(form);
29279         var n = '';
29280         Roo.each(form.allItems, function (fe) {
29281             
29282             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29283             if (this.findField(n)) { // already added..
29284                 return;
29285             }
29286             var add = new Roo.form.Hidden({
29287                 name : n
29288             });
29289             add.render(this.el);
29290             
29291             this.add( add );
29292         }, this);
29293         
29294     },
29295     /**
29296      * Mark fields in this form invalid in bulk.
29297      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29298      * @return {BasicForm} this
29299      */
29300     markInvalid : function(errors){
29301         if(errors instanceof Array){
29302             for(var i = 0, len = errors.length; i < len; i++){
29303                 var fieldError = errors[i];
29304                 var f = this.findField(fieldError.id);
29305                 if(f){
29306                     f.markInvalid(fieldError.msg);
29307                 }
29308             }
29309         }else{
29310             var field, id;
29311             for(id in errors){
29312                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29313                     field.markInvalid(errors[id]);
29314                 }
29315             }
29316         }
29317         Roo.each(this.childForms || [], function (f) {
29318             f.markInvalid(errors);
29319         });
29320         
29321         return this;
29322     },
29323
29324     /**
29325      * Set values for fields in this form in bulk.
29326      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29327      * @return {BasicForm} this
29328      */
29329     setValues : function(values){
29330         if(values instanceof Array){ // array of objects
29331             for(var i = 0, len = values.length; i < len; i++){
29332                 var v = values[i];
29333                 var f = this.findField(v.id);
29334                 if(f){
29335                     f.setValue(v.value);
29336                     if(this.trackResetOnLoad){
29337                         f.originalValue = f.getValue();
29338                     }
29339                 }
29340             }
29341         }else{ // object hash
29342             var field, id;
29343             for(id in values){
29344                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29345                     
29346                     if (field.setFromData && 
29347                         field.valueField && 
29348                         field.displayField &&
29349                         // combos' with local stores can 
29350                         // be queried via setValue()
29351                         // to set their value..
29352                         (field.store && !field.store.isLocal)
29353                         ) {
29354                         // it's a combo
29355                         var sd = { };
29356                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29357                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29358                         field.setFromData(sd);
29359                         
29360                     } else {
29361                         field.setValue(values[id]);
29362                     }
29363                     
29364                     
29365                     if(this.trackResetOnLoad){
29366                         field.originalValue = field.getValue();
29367                     }
29368                 }
29369             }
29370         }
29371         this.resetHasChanged();
29372         
29373         
29374         Roo.each(this.childForms || [], function (f) {
29375             f.setValues(values);
29376             f.resetHasChanged();
29377         });
29378                 
29379         return this;
29380     },
29381  
29382     /**
29383      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29384      * they are returned as an array.
29385      * @param {Boolean} asString
29386      * @return {Object}
29387      */
29388     getValues : function(asString)
29389     {
29390         if (this.childForms) {
29391             // copy values from the child forms
29392             Roo.each(this.childForms, function (f) {
29393                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29394             }, this);
29395         }
29396         
29397         // use formdata
29398         if (typeof(FormData) != 'undefined' && asString !== true) {
29399             // this relies on a 'recent' version of chrome apparently...
29400             try {
29401                 var fd = (new FormData(this.el.dom)).entries();
29402                 var ret = {};
29403                 var ent = fd.next();
29404                 while (!ent.done) {
29405                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29406                     ent = fd.next();
29407                 };
29408                 return ret;
29409             } catch(e) {
29410                 
29411             }
29412             
29413         }
29414         
29415         
29416         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29417         if(asString === true){
29418             return fs;
29419         }
29420         return Roo.urlDecode(fs);
29421     },
29422     
29423     /**
29424      * Returns the fields in this form as an object with key/value pairs. 
29425      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29426      * Normally this will not return readOnly data 
29427      * @param {Boolean} with_readonly return readonly field data.
29428      * @return {Object}
29429      */
29430     getFieldValues : function(with_readonly)
29431     {
29432         if (this.childForms) {
29433             // copy values from the child forms
29434             // should this call getFieldValues - probably not as we do not currently copy
29435             // hidden fields when we generate..
29436             Roo.each(this.childForms, function (f) {
29437                 this.setValues(f.getFieldValues());
29438             }, this);
29439         }
29440         
29441         var ret = {};
29442         this.items.each(function(f){
29443             
29444             if (f.readOnly && with_readonly !== true) {
29445                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29446                         // if a subform contains a copy of them.
29447                         // if you have subforms with the same editable data, you will need to copy the data back
29448                         // and forth.
29449             }
29450             
29451             if (!f.getName()) {
29452                 return;
29453             }
29454             var v = f.getValue();
29455             if (f.inputType =='radio') {
29456                 if (typeof(ret[f.getName()]) == 'undefined') {
29457                     ret[f.getName()] = ''; // empty..
29458                 }
29459                 
29460                 if (!f.el.dom.checked) {
29461                     return;
29462                     
29463                 }
29464                 v = f.el.dom.value;
29465                 
29466             }
29467             
29468             // not sure if this supported any more..
29469             if ((typeof(v) == 'object') && f.getRawValue) {
29470                 v = f.getRawValue() ; // dates..
29471             }
29472             // combo boxes where name != hiddenName...
29473             if (f.name != f.getName()) {
29474                 ret[f.name] = f.getRawValue();
29475             }
29476             ret[f.getName()] = v;
29477         });
29478         
29479         return ret;
29480     },
29481
29482     /**
29483      * Clears all invalid messages in this form.
29484      * @return {BasicForm} this
29485      */
29486     clearInvalid : function(){
29487         this.items.each(function(f){
29488            f.clearInvalid();
29489         });
29490         
29491         Roo.each(this.childForms || [], function (f) {
29492             f.clearInvalid();
29493         });
29494         
29495         
29496         return this;
29497     },
29498
29499     /**
29500      * Resets this form.
29501      * @return {BasicForm} this
29502      */
29503     reset : function(){
29504         this.items.each(function(f){
29505             f.reset();
29506         });
29507         
29508         Roo.each(this.childForms || [], function (f) {
29509             f.reset();
29510         });
29511         this.resetHasChanged();
29512         
29513         return this;
29514     },
29515
29516     /**
29517      * Add Roo.form components to this form.
29518      * @param {Field} field1
29519      * @param {Field} field2 (optional)
29520      * @param {Field} etc (optional)
29521      * @return {BasicForm} this
29522      */
29523     add : function(){
29524         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29525         return this;
29526     },
29527
29528
29529     /**
29530      * Removes a field from the items collection (does NOT remove its markup).
29531      * @param {Field} field
29532      * @return {BasicForm} this
29533      */
29534     remove : function(field){
29535         this.items.remove(field);
29536         return this;
29537     },
29538
29539     /**
29540      * Looks at the fields in this form, checks them for an id attribute,
29541      * and calls applyTo on the existing dom element with that id.
29542      * @return {BasicForm} this
29543      */
29544     render : function(){
29545         this.items.each(function(f){
29546             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29547                 f.applyTo(f.id);
29548             }
29549         });
29550         return this;
29551     },
29552
29553     /**
29554      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29555      * @param {Object} values
29556      * @return {BasicForm} this
29557      */
29558     applyToFields : function(o){
29559         this.items.each(function(f){
29560            Roo.apply(f, o);
29561         });
29562         return this;
29563     },
29564
29565     /**
29566      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29567      * @param {Object} values
29568      * @return {BasicForm} this
29569      */
29570     applyIfToFields : function(o){
29571         this.items.each(function(f){
29572            Roo.applyIf(f, o);
29573         });
29574         return this;
29575     }
29576 });
29577
29578 // back compat
29579 Roo.BasicForm = Roo.form.BasicForm;
29580
29581 Roo.apply(Roo.form.BasicForm, {
29582     
29583     popover : {
29584         
29585         padding : 5,
29586         
29587         isApplied : false,
29588         
29589         isMasked : false,
29590         
29591         form : false,
29592         
29593         target : false,
29594         
29595         intervalID : false,
29596         
29597         maskEl : false,
29598         
29599         apply : function()
29600         {
29601             if(this.isApplied){
29602                 return;
29603             }
29604             
29605             this.maskEl = {
29606                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29607                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29608                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29609                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29610             };
29611             
29612             this.maskEl.top.enableDisplayMode("block");
29613             this.maskEl.left.enableDisplayMode("block");
29614             this.maskEl.bottom.enableDisplayMode("block");
29615             this.maskEl.right.enableDisplayMode("block");
29616             
29617             Roo.get(document.body).on('click', function(){
29618                 this.unmask();
29619             }, this);
29620             
29621             Roo.get(document.body).on('touchstart', function(){
29622                 this.unmask();
29623             }, this);
29624             
29625             this.isApplied = true
29626         },
29627         
29628         mask : function(form, target)
29629         {
29630             this.form = form;
29631             
29632             this.target = target;
29633             
29634             if(!this.form.errorMask || !target.el){
29635                 return;
29636             }
29637             
29638             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29639             
29640             var ot = this.target.el.calcOffsetsTo(scrollable);
29641             
29642             var scrollTo = ot[1] - this.form.maskOffset;
29643             
29644             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29645             
29646             scrollable.scrollTo('top', scrollTo);
29647             
29648             var el = this.target.wrap || this.target.el;
29649             
29650             var box = el.getBox();
29651             
29652             this.maskEl.top.setStyle('position', 'absolute');
29653             this.maskEl.top.setStyle('z-index', 10000);
29654             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29655             this.maskEl.top.setLeft(0);
29656             this.maskEl.top.setTop(0);
29657             this.maskEl.top.show();
29658             
29659             this.maskEl.left.setStyle('position', 'absolute');
29660             this.maskEl.left.setStyle('z-index', 10000);
29661             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29662             this.maskEl.left.setLeft(0);
29663             this.maskEl.left.setTop(box.y - this.padding);
29664             this.maskEl.left.show();
29665
29666             this.maskEl.bottom.setStyle('position', 'absolute');
29667             this.maskEl.bottom.setStyle('z-index', 10000);
29668             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29669             this.maskEl.bottom.setLeft(0);
29670             this.maskEl.bottom.setTop(box.bottom + this.padding);
29671             this.maskEl.bottom.show();
29672
29673             this.maskEl.right.setStyle('position', 'absolute');
29674             this.maskEl.right.setStyle('z-index', 10000);
29675             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29676             this.maskEl.right.setLeft(box.right + this.padding);
29677             this.maskEl.right.setTop(box.y - this.padding);
29678             this.maskEl.right.show();
29679
29680             this.intervalID = window.setInterval(function() {
29681                 Roo.form.BasicForm.popover.unmask();
29682             }, 10000);
29683
29684             window.onwheel = function(){ return false;};
29685             
29686             (function(){ this.isMasked = true; }).defer(500, this);
29687             
29688         },
29689         
29690         unmask : function()
29691         {
29692             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29693                 return;
29694             }
29695             
29696             this.maskEl.top.setStyle('position', 'absolute');
29697             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29698             this.maskEl.top.hide();
29699
29700             this.maskEl.left.setStyle('position', 'absolute');
29701             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29702             this.maskEl.left.hide();
29703
29704             this.maskEl.bottom.setStyle('position', 'absolute');
29705             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29706             this.maskEl.bottom.hide();
29707
29708             this.maskEl.right.setStyle('position', 'absolute');
29709             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29710             this.maskEl.right.hide();
29711             
29712             window.onwheel = function(){ return true;};
29713             
29714             if(this.intervalID){
29715                 window.clearInterval(this.intervalID);
29716                 this.intervalID = false;
29717             }
29718             
29719             this.isMasked = false;
29720             
29721         }
29722         
29723     }
29724     
29725 });/*
29726  * Based on:
29727  * Ext JS Library 1.1.1
29728  * Copyright(c) 2006-2007, Ext JS, LLC.
29729  *
29730  * Originally Released Under LGPL - original licence link has changed is not relivant.
29731  *
29732  * Fork - LGPL
29733  * <script type="text/javascript">
29734  */
29735
29736 /**
29737  * @class Roo.form.Form
29738  * @extends Roo.form.BasicForm
29739  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29740  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29741  * @constructor
29742  * @param {Object} config Configuration options
29743  */
29744 Roo.form.Form = function(config){
29745     var xitems =  [];
29746     if (config.items) {
29747         xitems = config.items;
29748         delete config.items;
29749     }
29750    
29751     
29752     Roo.form.Form.superclass.constructor.call(this, null, config);
29753     this.url = this.url || this.action;
29754     if(!this.root){
29755         this.root = new Roo.form.Layout(Roo.applyIf({
29756             id: Roo.id()
29757         }, config));
29758     }
29759     this.active = this.root;
29760     /**
29761      * Array of all the buttons that have been added to this form via {@link addButton}
29762      * @type Array
29763      */
29764     this.buttons = [];
29765     this.allItems = [];
29766     this.addEvents({
29767         /**
29768          * @event clientvalidation
29769          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29770          * @param {Form} this
29771          * @param {Boolean} valid true if the form has passed client-side validation
29772          */
29773         clientvalidation: true,
29774         /**
29775          * @event rendered
29776          * Fires when the form is rendered
29777          * @param {Roo.form.Form} form
29778          */
29779         rendered : true
29780     });
29781     
29782     if (this.progressUrl) {
29783             // push a hidden field onto the list of fields..
29784             this.addxtype( {
29785                     xns: Roo.form, 
29786                     xtype : 'Hidden', 
29787                     name : 'UPLOAD_IDENTIFIER' 
29788             });
29789         }
29790         
29791     
29792     Roo.each(xitems, this.addxtype, this);
29793     
29794 };
29795
29796 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29797      /**
29798      * @cfg {Roo.Button} buttons[] buttons at bottom of form
29799      */
29800     
29801     /**
29802      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29803      */
29804     /**
29805      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29806      */
29807     /**
29808      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29809      */
29810     buttonAlign:'center',
29811
29812     /**
29813      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29814      */
29815     minButtonWidth:75,
29816
29817     /**
29818      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29819      * This property cascades to child containers if not set.
29820      */
29821     labelAlign:'left',
29822
29823     /**
29824      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29825      * fires a looping event with that state. This is required to bind buttons to the valid
29826      * state using the config value formBind:true on the button.
29827      */
29828     monitorValid : false,
29829
29830     /**
29831      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29832      */
29833     monitorPoll : 200,
29834     
29835     /**
29836      * @cfg {String} progressUrl - Url to return progress data 
29837      */
29838     
29839     progressUrl : false,
29840     /**
29841      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
29842      * sending a formdata with extra parameters - eg uploaded elements.
29843      */
29844     
29845     formData : false,
29846     
29847     /**
29848      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29849      * fields are added and the column is closed. If no fields are passed the column remains open
29850      * until end() is called.
29851      * @param {Object} config The config to pass to the column
29852      * @param {Field} field1 (optional)
29853      * @param {Field} field2 (optional)
29854      * @param {Field} etc (optional)
29855      * @return Column The column container object
29856      */
29857     column : function(c){
29858         var col = new Roo.form.Column(c);
29859         this.start(col);
29860         if(arguments.length > 1){ // duplicate code required because of Opera
29861             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29862             this.end();
29863         }
29864         return col;
29865     },
29866
29867     /**
29868      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29869      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29870      * until end() is called.
29871      * @param {Object} config The config to pass to the fieldset
29872      * @param {Field} field1 (optional)
29873      * @param {Field} field2 (optional)
29874      * @param {Field} etc (optional)
29875      * @return FieldSet The fieldset container object
29876      */
29877     fieldset : function(c){
29878         var fs = new Roo.form.FieldSet(c);
29879         this.start(fs);
29880         if(arguments.length > 1){ // duplicate code required because of Opera
29881             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29882             this.end();
29883         }
29884         return fs;
29885     },
29886
29887     /**
29888      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29889      * fields are added and the container is closed. If no fields are passed the container remains open
29890      * until end() is called.
29891      * @param {Object} config The config to pass to the Layout
29892      * @param {Field} field1 (optional)
29893      * @param {Field} field2 (optional)
29894      * @param {Field} etc (optional)
29895      * @return Layout The container object
29896      */
29897     container : function(c){
29898         var l = new Roo.form.Layout(c);
29899         this.start(l);
29900         if(arguments.length > 1){ // duplicate code required because of Opera
29901             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29902             this.end();
29903         }
29904         return l;
29905     },
29906
29907     /**
29908      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29909      * @param {Object} container A Roo.form.Layout or subclass of Layout
29910      * @return {Form} this
29911      */
29912     start : function(c){
29913         // cascade label info
29914         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29915         this.active.stack.push(c);
29916         c.ownerCt = this.active;
29917         this.active = c;
29918         return this;
29919     },
29920
29921     /**
29922      * Closes the current open container
29923      * @return {Form} this
29924      */
29925     end : function(){
29926         if(this.active == this.root){
29927             return this;
29928         }
29929         this.active = this.active.ownerCt;
29930         return this;
29931     },
29932
29933     /**
29934      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29935      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29936      * as the label of the field.
29937      * @param {Field} field1
29938      * @param {Field} field2 (optional)
29939      * @param {Field} etc. (optional)
29940      * @return {Form} this
29941      */
29942     add : function(){
29943         this.active.stack.push.apply(this.active.stack, arguments);
29944         this.allItems.push.apply(this.allItems,arguments);
29945         var r = [];
29946         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29947             if(a[i].isFormField){
29948                 r.push(a[i]);
29949             }
29950         }
29951         if(r.length > 0){
29952             Roo.form.Form.superclass.add.apply(this, r);
29953         }
29954         return this;
29955     },
29956     
29957
29958     
29959     
29960     
29961      /**
29962      * Find any element that has been added to a form, using it's ID or name
29963      * This can include framesets, columns etc. along with regular fields..
29964      * @param {String} id - id or name to find.
29965      
29966      * @return {Element} e - or false if nothing found.
29967      */
29968     findbyId : function(id)
29969     {
29970         var ret = false;
29971         if (!id) {
29972             return ret;
29973         }
29974         Roo.each(this.allItems, function(f){
29975             if (f.id == id || f.name == id ){
29976                 ret = f;
29977                 return false;
29978             }
29979         });
29980         return ret;
29981     },
29982
29983     
29984     
29985     /**
29986      * Render this form into the passed container. This should only be called once!
29987      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29988      * @return {Form} this
29989      */
29990     render : function(ct)
29991     {
29992         
29993         
29994         
29995         ct = Roo.get(ct);
29996         var o = this.autoCreate || {
29997             tag: 'form',
29998             method : this.method || 'POST',
29999             id : this.id || Roo.id()
30000         };
30001         this.initEl(ct.createChild(o));
30002
30003         this.root.render(this.el);
30004         
30005        
30006              
30007         this.items.each(function(f){
30008             f.render('x-form-el-'+f.id);
30009         });
30010
30011         if(this.buttons.length > 0){
30012             // tables are required to maintain order and for correct IE layout
30013             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30014                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30015                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30016             }}, null, true);
30017             var tr = tb.getElementsByTagName('tr')[0];
30018             for(var i = 0, len = this.buttons.length; i < len; i++) {
30019                 var b = this.buttons[i];
30020                 var td = document.createElement('td');
30021                 td.className = 'x-form-btn-td';
30022                 b.render(tr.appendChild(td));
30023             }
30024         }
30025         if(this.monitorValid){ // initialize after render
30026             this.startMonitoring();
30027         }
30028         this.fireEvent('rendered', this);
30029         return this;
30030     },
30031
30032     /**
30033      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30034      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30035      * object or a valid Roo.DomHelper element config
30036      * @param {Function} handler The function called when the button is clicked
30037      * @param {Object} scope (optional) The scope of the handler function
30038      * @return {Roo.Button}
30039      */
30040     addButton : function(config, handler, scope){
30041         var bc = {
30042             handler: handler,
30043             scope: scope,
30044             minWidth: this.minButtonWidth,
30045             hideParent:true
30046         };
30047         if(typeof config == "string"){
30048             bc.text = config;
30049         }else{
30050             Roo.apply(bc, config);
30051         }
30052         var btn = new Roo.Button(null, bc);
30053         this.buttons.push(btn);
30054         return btn;
30055     },
30056
30057      /**
30058      * Adds a series of form elements (using the xtype property as the factory method.
30059      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30060      * @param {Object} config 
30061      */
30062     
30063     addxtype : function()
30064     {
30065         var ar = Array.prototype.slice.call(arguments, 0);
30066         var ret = false;
30067         for(var i = 0; i < ar.length; i++) {
30068             if (!ar[i]) {
30069                 continue; // skip -- if this happends something invalid got sent, we 
30070                 // should ignore it, as basically that interface element will not show up
30071                 // and that should be pretty obvious!!
30072             }
30073             
30074             if (Roo.form[ar[i].xtype]) {
30075                 ar[i].form = this;
30076                 var fe = Roo.factory(ar[i], Roo.form);
30077                 if (!ret) {
30078                     ret = fe;
30079                 }
30080                 fe.form = this;
30081                 if (fe.store) {
30082                     fe.store.form = this;
30083                 }
30084                 if (fe.isLayout) {  
30085                          
30086                     this.start(fe);
30087                     this.allItems.push(fe);
30088                     if (fe.items && fe.addxtype) {
30089                         fe.addxtype.apply(fe, fe.items);
30090                         delete fe.items;
30091                     }
30092                      this.end();
30093                     continue;
30094                 }
30095                 
30096                 
30097                  
30098                 this.add(fe);
30099               //  console.log('adding ' + ar[i].xtype);
30100             }
30101             if (ar[i].xtype == 'Button') {  
30102                 //console.log('adding button');
30103                 //console.log(ar[i]);
30104                 this.addButton(ar[i]);
30105                 this.allItems.push(fe);
30106                 continue;
30107             }
30108             
30109             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30110                 alert('end is not supported on xtype any more, use items');
30111             //    this.end();
30112             //    //console.log('adding end');
30113             }
30114             
30115         }
30116         return ret;
30117     },
30118     
30119     /**
30120      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30121      * option "monitorValid"
30122      */
30123     startMonitoring : function(){
30124         if(!this.bound){
30125             this.bound = true;
30126             Roo.TaskMgr.start({
30127                 run : this.bindHandler,
30128                 interval : this.monitorPoll || 200,
30129                 scope: this
30130             });
30131         }
30132     },
30133
30134     /**
30135      * Stops monitoring of the valid state of this form
30136      */
30137     stopMonitoring : function(){
30138         this.bound = false;
30139     },
30140
30141     // private
30142     bindHandler : function(){
30143         if(!this.bound){
30144             return false; // stops binding
30145         }
30146         var valid = true;
30147         this.items.each(function(f){
30148             if(!f.isValid(true)){
30149                 valid = false;
30150                 return false;
30151             }
30152         });
30153         for(var i = 0, len = this.buttons.length; i < len; i++){
30154             var btn = this.buttons[i];
30155             if(btn.formBind === true && btn.disabled === valid){
30156                 btn.setDisabled(!valid);
30157             }
30158         }
30159         this.fireEvent('clientvalidation', this, valid);
30160     }
30161     
30162     
30163     
30164     
30165     
30166     
30167     
30168     
30169 });
30170
30171
30172 // back compat
30173 Roo.Form = Roo.form.Form;
30174 /*
30175  * Based on:
30176  * Ext JS Library 1.1.1
30177  * Copyright(c) 2006-2007, Ext JS, LLC.
30178  *
30179  * Originally Released Under LGPL - original licence link has changed is not relivant.
30180  *
30181  * Fork - LGPL
30182  * <script type="text/javascript">
30183  */
30184
30185 // as we use this in bootstrap.
30186 Roo.namespace('Roo.form');
30187  /**
30188  * @class Roo.form.Action
30189  * Internal Class used to handle form actions
30190  * @constructor
30191  * @param {Roo.form.BasicForm} el The form element or its id
30192  * @param {Object} config Configuration options
30193  */
30194
30195  
30196  
30197 // define the action interface
30198 Roo.form.Action = function(form, options){
30199     this.form = form;
30200     this.options = options || {};
30201 };
30202 /**
30203  * Client Validation Failed
30204  * @const 
30205  */
30206 Roo.form.Action.CLIENT_INVALID = 'client';
30207 /**
30208  * Server Validation Failed
30209  * @const 
30210  */
30211 Roo.form.Action.SERVER_INVALID = 'server';
30212  /**
30213  * Connect to Server Failed
30214  * @const 
30215  */
30216 Roo.form.Action.CONNECT_FAILURE = 'connect';
30217 /**
30218  * Reading Data from Server Failed
30219  * @const 
30220  */
30221 Roo.form.Action.LOAD_FAILURE = 'load';
30222
30223 Roo.form.Action.prototype = {
30224     type : 'default',
30225     failureType : undefined,
30226     response : undefined,
30227     result : undefined,
30228
30229     // interface method
30230     run : function(options){
30231
30232     },
30233
30234     // interface method
30235     success : function(response){
30236
30237     },
30238
30239     // interface method
30240     handleResponse : function(response){
30241
30242     },
30243
30244     // default connection failure
30245     failure : function(response){
30246         
30247         this.response = response;
30248         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30249         this.form.afterAction(this, false);
30250     },
30251
30252     processResponse : function(response){
30253         this.response = response;
30254         if(!response.responseText){
30255             return true;
30256         }
30257         this.result = this.handleResponse(response);
30258         return this.result;
30259     },
30260
30261     // utility functions used internally
30262     getUrl : function(appendParams){
30263         var url = this.options.url || this.form.url || this.form.el.dom.action;
30264         if(appendParams){
30265             var p = this.getParams();
30266             if(p){
30267                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30268             }
30269         }
30270         return url;
30271     },
30272
30273     getMethod : function(){
30274         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30275     },
30276
30277     getParams : function(){
30278         var bp = this.form.baseParams;
30279         var p = this.options.params;
30280         if(p){
30281             if(typeof p == "object"){
30282                 p = Roo.urlEncode(Roo.applyIf(p, bp));
30283             }else if(typeof p == 'string' && bp){
30284                 p += '&' + Roo.urlEncode(bp);
30285             }
30286         }else if(bp){
30287             p = Roo.urlEncode(bp);
30288         }
30289         return p;
30290     },
30291
30292     createCallback : function(){
30293         return {
30294             success: this.success,
30295             failure: this.failure,
30296             scope: this,
30297             timeout: (this.form.timeout*1000),
30298             upload: this.form.fileUpload ? this.success : undefined
30299         };
30300     }
30301 };
30302
30303 Roo.form.Action.Submit = function(form, options){
30304     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30305 };
30306
30307 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30308     type : 'submit',
30309
30310     haveProgress : false,
30311     uploadComplete : false,
30312     
30313     // uploadProgress indicator.
30314     uploadProgress : function()
30315     {
30316         if (!this.form.progressUrl) {
30317             return;
30318         }
30319         
30320         if (!this.haveProgress) {
30321             Roo.MessageBox.progress("Uploading", "Uploading");
30322         }
30323         if (this.uploadComplete) {
30324            Roo.MessageBox.hide();
30325            return;
30326         }
30327         
30328         this.haveProgress = true;
30329    
30330         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30331         
30332         var c = new Roo.data.Connection();
30333         c.request({
30334             url : this.form.progressUrl,
30335             params: {
30336                 id : uid
30337             },
30338             method: 'GET',
30339             success : function(req){
30340                //console.log(data);
30341                 var rdata = false;
30342                 var edata;
30343                 try  {
30344                    rdata = Roo.decode(req.responseText)
30345                 } catch (e) {
30346                     Roo.log("Invalid data from server..");
30347                     Roo.log(edata);
30348                     return;
30349                 }
30350                 if (!rdata || !rdata.success) {
30351                     Roo.log(rdata);
30352                     Roo.MessageBox.alert(Roo.encode(rdata));
30353                     return;
30354                 }
30355                 var data = rdata.data;
30356                 
30357                 if (this.uploadComplete) {
30358                    Roo.MessageBox.hide();
30359                    return;
30360                 }
30361                    
30362                 if (data){
30363                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30364                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30365                     );
30366                 }
30367                 this.uploadProgress.defer(2000,this);
30368             },
30369        
30370             failure: function(data) {
30371                 Roo.log('progress url failed ');
30372                 Roo.log(data);
30373             },
30374             scope : this
30375         });
30376            
30377     },
30378     
30379     
30380     run : function()
30381     {
30382         // run get Values on the form, so it syncs any secondary forms.
30383         this.form.getValues();
30384         
30385         var o = this.options;
30386         var method = this.getMethod();
30387         var isPost = method == 'POST';
30388         if(o.clientValidation === false || this.form.isValid()){
30389             
30390             if (this.form.progressUrl) {
30391                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30392                     (new Date() * 1) + '' + Math.random());
30393                     
30394             } 
30395             
30396             
30397             Roo.Ajax.request(Roo.apply(this.createCallback(), {
30398                 form:this.form.el.dom,
30399                 url:this.getUrl(!isPost),
30400                 method: method,
30401                 params:isPost ? this.getParams() : null,
30402                 isUpload: this.form.fileUpload,
30403                 formData : this.form.formData
30404             }));
30405             
30406             this.uploadProgress();
30407
30408         }else if (o.clientValidation !== false){ // client validation failed
30409             this.failureType = Roo.form.Action.CLIENT_INVALID;
30410             this.form.afterAction(this, false);
30411         }
30412     },
30413
30414     success : function(response)
30415     {
30416         this.uploadComplete= true;
30417         if (this.haveProgress) {
30418             Roo.MessageBox.hide();
30419         }
30420         
30421         
30422         var result = this.processResponse(response);
30423         if(result === true || result.success){
30424             this.form.afterAction(this, true);
30425             return;
30426         }
30427         if(result.errors){
30428             this.form.markInvalid(result.errors);
30429             this.failureType = Roo.form.Action.SERVER_INVALID;
30430         }
30431         this.form.afterAction(this, false);
30432     },
30433     failure : function(response)
30434     {
30435         this.uploadComplete= true;
30436         if (this.haveProgress) {
30437             Roo.MessageBox.hide();
30438         }
30439         
30440         this.response = response;
30441         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30442         this.form.afterAction(this, false);
30443     },
30444     
30445     handleResponse : function(response){
30446         if(this.form.errorReader){
30447             var rs = this.form.errorReader.read(response);
30448             var errors = [];
30449             if(rs.records){
30450                 for(var i = 0, len = rs.records.length; i < len; i++) {
30451                     var r = rs.records[i];
30452                     errors[i] = r.data;
30453                 }
30454             }
30455             if(errors.length < 1){
30456                 errors = null;
30457             }
30458             return {
30459                 success : rs.success,
30460                 errors : errors
30461             };
30462         }
30463         var ret = false;
30464         try {
30465             ret = Roo.decode(response.responseText);
30466         } catch (e) {
30467             ret = {
30468                 success: false,
30469                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30470                 errors : []
30471             };
30472         }
30473         return ret;
30474         
30475     }
30476 });
30477
30478
30479 Roo.form.Action.Load = function(form, options){
30480     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30481     this.reader = this.form.reader;
30482 };
30483
30484 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30485     type : 'load',
30486
30487     run : function(){
30488         
30489         Roo.Ajax.request(Roo.apply(
30490                 this.createCallback(), {
30491                     method:this.getMethod(),
30492                     url:this.getUrl(false),
30493                     params:this.getParams()
30494         }));
30495     },
30496
30497     success : function(response){
30498         
30499         var result = this.processResponse(response);
30500         if(result === true || !result.success || !result.data){
30501             this.failureType = Roo.form.Action.LOAD_FAILURE;
30502             this.form.afterAction(this, false);
30503             return;
30504         }
30505         this.form.clearInvalid();
30506         this.form.setValues(result.data);
30507         this.form.afterAction(this, true);
30508     },
30509
30510     handleResponse : function(response){
30511         if(this.form.reader){
30512             var rs = this.form.reader.read(response);
30513             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30514             return {
30515                 success : rs.success,
30516                 data : data
30517             };
30518         }
30519         return Roo.decode(response.responseText);
30520     }
30521 });
30522
30523 Roo.form.Action.ACTION_TYPES = {
30524     'load' : Roo.form.Action.Load,
30525     'submit' : Roo.form.Action.Submit
30526 };/*
30527  * Based on:
30528  * Ext JS Library 1.1.1
30529  * Copyright(c) 2006-2007, Ext JS, LLC.
30530  *
30531  * Originally Released Under LGPL - original licence link has changed is not relivant.
30532  *
30533  * Fork - LGPL
30534  * <script type="text/javascript">
30535  */
30536  
30537 /**
30538  * @class Roo.form.Layout
30539  * @extends Roo.Component
30540  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30541  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30542  * @constructor
30543  * @param {Object} config Configuration options
30544  */
30545 Roo.form.Layout = function(config){
30546     var xitems = [];
30547     if (config.items) {
30548         xitems = config.items;
30549         delete config.items;
30550     }
30551     Roo.form.Layout.superclass.constructor.call(this, config);
30552     this.stack = [];
30553     Roo.each(xitems, this.addxtype, this);
30554      
30555 };
30556
30557 Roo.extend(Roo.form.Layout, Roo.Component, {
30558     /**
30559      * @cfg {String/Object} autoCreate
30560      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30561      */
30562     /**
30563      * @cfg {String/Object/Function} style
30564      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30565      * a function which returns such a specification.
30566      */
30567     /**
30568      * @cfg {String} labelAlign
30569      * Valid values are "left," "top" and "right" (defaults to "left")
30570      */
30571     /**
30572      * @cfg {Number} labelWidth
30573      * Fixed width in pixels of all field labels (defaults to undefined)
30574      */
30575     /**
30576      * @cfg {Boolean} clear
30577      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30578      */
30579     clear : true,
30580     /**
30581      * @cfg {String} labelSeparator
30582      * The separator to use after field labels (defaults to ':')
30583      */
30584     labelSeparator : ':',
30585     /**
30586      * @cfg {Boolean} hideLabels
30587      * True to suppress the display of field labels in this layout (defaults to false)
30588      */
30589     hideLabels : false,
30590
30591     // private
30592     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30593     
30594     isLayout : true,
30595     
30596     // private
30597     onRender : function(ct, position){
30598         if(this.el){ // from markup
30599             this.el = Roo.get(this.el);
30600         }else {  // generate
30601             var cfg = this.getAutoCreate();
30602             this.el = ct.createChild(cfg, position);
30603         }
30604         if(this.style){
30605             this.el.applyStyles(this.style);
30606         }
30607         if(this.labelAlign){
30608             this.el.addClass('x-form-label-'+this.labelAlign);
30609         }
30610         if(this.hideLabels){
30611             this.labelStyle = "display:none";
30612             this.elementStyle = "padding-left:0;";
30613         }else{
30614             if(typeof this.labelWidth == 'number'){
30615                 this.labelStyle = "width:"+this.labelWidth+"px;";
30616                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30617             }
30618             if(this.labelAlign == 'top'){
30619                 this.labelStyle = "width:auto;";
30620                 this.elementStyle = "padding-left:0;";
30621             }
30622         }
30623         var stack = this.stack;
30624         var slen = stack.length;
30625         if(slen > 0){
30626             if(!this.fieldTpl){
30627                 var t = new Roo.Template(
30628                     '<div class="x-form-item {5}">',
30629                         '<label for="{0}" style="{2}">{1}{4}</label>',
30630                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30631                         '</div>',
30632                     '</div><div class="x-form-clear-left"></div>'
30633                 );
30634                 t.disableFormats = true;
30635                 t.compile();
30636                 Roo.form.Layout.prototype.fieldTpl = t;
30637             }
30638             for(var i = 0; i < slen; i++) {
30639                 if(stack[i].isFormField){
30640                     this.renderField(stack[i]);
30641                 }else{
30642                     this.renderComponent(stack[i]);
30643                 }
30644             }
30645         }
30646         if(this.clear){
30647             this.el.createChild({cls:'x-form-clear'});
30648         }
30649     },
30650
30651     // private
30652     renderField : function(f){
30653         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30654                f.id, //0
30655                f.fieldLabel, //1
30656                f.labelStyle||this.labelStyle||'', //2
30657                this.elementStyle||'', //3
30658                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30659                f.itemCls||this.itemCls||''  //5
30660        ], true).getPrevSibling());
30661     },
30662
30663     // private
30664     renderComponent : function(c){
30665         c.render(c.isLayout ? this.el : this.el.createChild());    
30666     },
30667     /**
30668      * Adds a object form elements (using the xtype property as the factory method.)
30669      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30670      * @param {Object} config 
30671      */
30672     addxtype : function(o)
30673     {
30674         // create the lement.
30675         o.form = this.form;
30676         var fe = Roo.factory(o, Roo.form);
30677         this.form.allItems.push(fe);
30678         this.stack.push(fe);
30679         
30680         if (fe.isFormField) {
30681             this.form.items.add(fe);
30682         }
30683          
30684         return fe;
30685     }
30686 });
30687
30688 /**
30689  * @class Roo.form.Column
30690  * @extends Roo.form.Layout
30691  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30692  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30693  * @constructor
30694  * @param {Object} config Configuration options
30695  */
30696 Roo.form.Column = function(config){
30697     Roo.form.Column.superclass.constructor.call(this, config);
30698 };
30699
30700 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30701     /**
30702      * @cfg {Number/String} width
30703      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30704      */
30705     /**
30706      * @cfg {String/Object} autoCreate
30707      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30708      */
30709
30710     // private
30711     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30712
30713     // private
30714     onRender : function(ct, position){
30715         Roo.form.Column.superclass.onRender.call(this, ct, position);
30716         if(this.width){
30717             this.el.setWidth(this.width);
30718         }
30719     }
30720 });
30721
30722
30723 /**
30724  * @class Roo.form.Row
30725  * @extends Roo.form.Layout
30726  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30727  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30728  * @constructor
30729  * @param {Object} config Configuration options
30730  */
30731
30732  
30733 Roo.form.Row = function(config){
30734     Roo.form.Row.superclass.constructor.call(this, config);
30735 };
30736  
30737 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30738       /**
30739      * @cfg {Number/String} width
30740      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30741      */
30742     /**
30743      * @cfg {Number/String} height
30744      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30745      */
30746     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30747     
30748     padWidth : 20,
30749     // private
30750     onRender : function(ct, position){
30751         //console.log('row render');
30752         if(!this.rowTpl){
30753             var t = new Roo.Template(
30754                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30755                     '<label for="{0}" style="{2}">{1}{4}</label>',
30756                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30757                     '</div>',
30758                 '</div>'
30759             );
30760             t.disableFormats = true;
30761             t.compile();
30762             Roo.form.Layout.prototype.rowTpl = t;
30763         }
30764         this.fieldTpl = this.rowTpl;
30765         
30766         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30767         var labelWidth = 100;
30768         
30769         if ((this.labelAlign != 'top')) {
30770             if (typeof this.labelWidth == 'number') {
30771                 labelWidth = this.labelWidth
30772             }
30773             this.padWidth =  20 + labelWidth;
30774             
30775         }
30776         
30777         Roo.form.Column.superclass.onRender.call(this, ct, position);
30778         if(this.width){
30779             this.el.setWidth(this.width);
30780         }
30781         if(this.height){
30782             this.el.setHeight(this.height);
30783         }
30784     },
30785     
30786     // private
30787     renderField : function(f){
30788         f.fieldEl = this.fieldTpl.append(this.el, [
30789                f.id, f.fieldLabel,
30790                f.labelStyle||this.labelStyle||'',
30791                this.elementStyle||'',
30792                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30793                f.itemCls||this.itemCls||'',
30794                f.width ? f.width + this.padWidth : 160 + this.padWidth
30795        ],true);
30796     }
30797 });
30798  
30799
30800 /**
30801  * @class Roo.form.FieldSet
30802  * @extends Roo.form.Layout
30803  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
30804  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30805  * @constructor
30806  * @param {Object} config Configuration options
30807  */
30808 Roo.form.FieldSet = function(config){
30809     Roo.form.FieldSet.superclass.constructor.call(this, config);
30810 };
30811
30812 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30813     /**
30814      * @cfg {String} legend
30815      * The text to display as the legend for the FieldSet (defaults to '')
30816      */
30817     /**
30818      * @cfg {String/Object} autoCreate
30819      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30820      */
30821
30822     // private
30823     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30824
30825     // private
30826     onRender : function(ct, position){
30827         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30828         if(this.legend){
30829             this.setLegend(this.legend);
30830         }
30831     },
30832
30833     // private
30834     setLegend : function(text){
30835         if(this.rendered){
30836             this.el.child('legend').update(text);
30837         }
30838     }
30839 });/*
30840  * Based on:
30841  * Ext JS Library 1.1.1
30842  * Copyright(c) 2006-2007, Ext JS, LLC.
30843  *
30844  * Originally Released Under LGPL - original licence link has changed is not relivant.
30845  *
30846  * Fork - LGPL
30847  * <script type="text/javascript">
30848  */
30849 /**
30850  * @class Roo.form.VTypes
30851  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30852  * @static
30853  */
30854 Roo.form.VTypes = function(){
30855     // closure these in so they are only created once.
30856     var alpha = /^[a-zA-Z_]+$/;
30857     var alphanum = /^[a-zA-Z0-9_]+$/;
30858     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30859     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30860
30861     // All these messages and functions are configurable
30862     return {
30863         /**
30864          * The function used to validate email addresses
30865          * @param {String} value The email address
30866          */
30867         'email' : function(v){
30868             return email.test(v);
30869         },
30870         /**
30871          * The error text to display when the email validation function returns false
30872          * @type String
30873          */
30874         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30875         /**
30876          * The keystroke filter mask to be applied on email input
30877          * @type RegExp
30878          */
30879         'emailMask' : /[a-z0-9_\.\-@]/i,
30880
30881         /**
30882          * The function used to validate URLs
30883          * @param {String} value The URL
30884          */
30885         'url' : function(v){
30886             return url.test(v);
30887         },
30888         /**
30889          * The error text to display when the url validation function returns false
30890          * @type String
30891          */
30892         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30893         
30894         /**
30895          * The function used to validate alpha values
30896          * @param {String} value The value
30897          */
30898         'alpha' : function(v){
30899             return alpha.test(v);
30900         },
30901         /**
30902          * The error text to display when the alpha validation function returns false
30903          * @type String
30904          */
30905         'alphaText' : 'This field should only contain letters and _',
30906         /**
30907          * The keystroke filter mask to be applied on alpha input
30908          * @type RegExp
30909          */
30910         'alphaMask' : /[a-z_]/i,
30911
30912         /**
30913          * The function used to validate alphanumeric values
30914          * @param {String} value The value
30915          */
30916         'alphanum' : function(v){
30917             return alphanum.test(v);
30918         },
30919         /**
30920          * The error text to display when the alphanumeric validation function returns false
30921          * @type String
30922          */
30923         'alphanumText' : 'This field should only contain letters, numbers and _',
30924         /**
30925          * The keystroke filter mask to be applied on alphanumeric input
30926          * @type RegExp
30927          */
30928         'alphanumMask' : /[a-z0-9_]/i
30929     };
30930 }();//<script type="text/javascript">
30931
30932 /**
30933  * @class Roo.form.FCKeditor
30934  * @extends Roo.form.TextArea
30935  * Wrapper around the FCKEditor http://www.fckeditor.net
30936  * @constructor
30937  * Creates a new FCKeditor
30938  * @param {Object} config Configuration options
30939  */
30940 Roo.form.FCKeditor = function(config){
30941     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30942     this.addEvents({
30943          /**
30944          * @event editorinit
30945          * Fired when the editor is initialized - you can add extra handlers here..
30946          * @param {FCKeditor} this
30947          * @param {Object} the FCK object.
30948          */
30949         editorinit : true
30950     });
30951     
30952     
30953 };
30954 Roo.form.FCKeditor.editors = { };
30955 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30956 {
30957     //defaultAutoCreate : {
30958     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30959     //},
30960     // private
30961     /**
30962      * @cfg {Object} fck options - see fck manual for details.
30963      */
30964     fckconfig : false,
30965     
30966     /**
30967      * @cfg {Object} fck toolbar set (Basic or Default)
30968      */
30969     toolbarSet : 'Basic',
30970     /**
30971      * @cfg {Object} fck BasePath
30972      */ 
30973     basePath : '/fckeditor/',
30974     
30975     
30976     frame : false,
30977     
30978     value : '',
30979     
30980    
30981     onRender : function(ct, position)
30982     {
30983         if(!this.el){
30984             this.defaultAutoCreate = {
30985                 tag: "textarea",
30986                 style:"width:300px;height:60px;",
30987                 autocomplete: "new-password"
30988             };
30989         }
30990         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30991         /*
30992         if(this.grow){
30993             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30994             if(this.preventScrollbars){
30995                 this.el.setStyle("overflow", "hidden");
30996             }
30997             this.el.setHeight(this.growMin);
30998         }
30999         */
31000         //console.log('onrender' + this.getId() );
31001         Roo.form.FCKeditor.editors[this.getId()] = this;
31002          
31003
31004         this.replaceTextarea() ;
31005         
31006     },
31007     
31008     getEditor : function() {
31009         return this.fckEditor;
31010     },
31011     /**
31012      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
31013      * @param {Mixed} value The value to set
31014      */
31015     
31016     
31017     setValue : function(value)
31018     {
31019         //console.log('setValue: ' + value);
31020         
31021         if(typeof(value) == 'undefined') { // not sure why this is happending...
31022             return;
31023         }
31024         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31025         
31026         //if(!this.el || !this.getEditor()) {
31027         //    this.value = value;
31028             //this.setValue.defer(100,this,[value]);    
31029         //    return;
31030         //} 
31031         
31032         if(!this.getEditor()) {
31033             return;
31034         }
31035         
31036         this.getEditor().SetData(value);
31037         
31038         //
31039
31040     },
31041
31042     /**
31043      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
31044      * @return {Mixed} value The field value
31045      */
31046     getValue : function()
31047     {
31048         
31049         if (this.frame && this.frame.dom.style.display == 'none') {
31050             return Roo.form.FCKeditor.superclass.getValue.call(this);
31051         }
31052         
31053         if(!this.el || !this.getEditor()) {
31054            
31055            // this.getValue.defer(100,this); 
31056             return this.value;
31057         }
31058        
31059         
31060         var value=this.getEditor().GetData();
31061         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31062         return Roo.form.FCKeditor.superclass.getValue.call(this);
31063         
31064
31065     },
31066
31067     /**
31068      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
31069      * @return {Mixed} value The field value
31070      */
31071     getRawValue : function()
31072     {
31073         if (this.frame && this.frame.dom.style.display == 'none') {
31074             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31075         }
31076         
31077         if(!this.el || !this.getEditor()) {
31078             //this.getRawValue.defer(100,this); 
31079             return this.value;
31080             return;
31081         }
31082         
31083         
31084         
31085         var value=this.getEditor().GetData();
31086         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31087         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31088          
31089     },
31090     
31091     setSize : function(w,h) {
31092         
31093         
31094         
31095         //if (this.frame && this.frame.dom.style.display == 'none') {
31096         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31097         //    return;
31098         //}
31099         //if(!this.el || !this.getEditor()) {
31100         //    this.setSize.defer(100,this, [w,h]); 
31101         //    return;
31102         //}
31103         
31104         
31105         
31106         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31107         
31108         this.frame.dom.setAttribute('width', w);
31109         this.frame.dom.setAttribute('height', h);
31110         this.frame.setSize(w,h);
31111         
31112     },
31113     
31114     toggleSourceEdit : function(value) {
31115         
31116       
31117          
31118         this.el.dom.style.display = value ? '' : 'none';
31119         this.frame.dom.style.display = value ?  'none' : '';
31120         
31121     },
31122     
31123     
31124     focus: function(tag)
31125     {
31126         if (this.frame.dom.style.display == 'none') {
31127             return Roo.form.FCKeditor.superclass.focus.call(this);
31128         }
31129         if(!this.el || !this.getEditor()) {
31130             this.focus.defer(100,this, [tag]); 
31131             return;
31132         }
31133         
31134         
31135         
31136         
31137         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31138         this.getEditor().Focus();
31139         if (tgs.length) {
31140             if (!this.getEditor().Selection.GetSelection()) {
31141                 this.focus.defer(100,this, [tag]); 
31142                 return;
31143             }
31144             
31145             
31146             var r = this.getEditor().EditorDocument.createRange();
31147             r.setStart(tgs[0],0);
31148             r.setEnd(tgs[0],0);
31149             this.getEditor().Selection.GetSelection().removeAllRanges();
31150             this.getEditor().Selection.GetSelection().addRange(r);
31151             this.getEditor().Focus();
31152         }
31153         
31154     },
31155     
31156     
31157     
31158     replaceTextarea : function()
31159     {
31160         if ( document.getElementById( this.getId() + '___Frame' ) ) {
31161             return ;
31162         }
31163         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31164         //{
31165             // We must check the elements firstly using the Id and then the name.
31166         var oTextarea = document.getElementById( this.getId() );
31167         
31168         var colElementsByName = document.getElementsByName( this.getId() ) ;
31169          
31170         oTextarea.style.display = 'none' ;
31171
31172         if ( oTextarea.tabIndex ) {            
31173             this.TabIndex = oTextarea.tabIndex ;
31174         }
31175         
31176         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31177         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31178         this.frame = Roo.get(this.getId() + '___Frame')
31179     },
31180     
31181     _getConfigHtml : function()
31182     {
31183         var sConfig = '' ;
31184
31185         for ( var o in this.fckconfig ) {
31186             sConfig += sConfig.length > 0  ? '&amp;' : '';
31187             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31188         }
31189
31190         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31191     },
31192     
31193     
31194     _getIFrameHtml : function()
31195     {
31196         var sFile = 'fckeditor.html' ;
31197         /* no idea what this is about..
31198         try
31199         {
31200             if ( (/fcksource=true/i).test( window.top.location.search ) )
31201                 sFile = 'fckeditor.original.html' ;
31202         }
31203         catch (e) { 
31204         */
31205
31206         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31207         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
31208         
31209         
31210         var html = '<iframe id="' + this.getId() +
31211             '___Frame" src="' + sLink +
31212             '" width="' + this.width +
31213             '" height="' + this.height + '"' +
31214             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
31215             ' frameborder="0" scrolling="no"></iframe>' ;
31216
31217         return html ;
31218     },
31219     
31220     _insertHtmlBefore : function( html, element )
31221     {
31222         if ( element.insertAdjacentHTML )       {
31223             // IE
31224             element.insertAdjacentHTML( 'beforeBegin', html ) ;
31225         } else { // Gecko
31226             var oRange = document.createRange() ;
31227             oRange.setStartBefore( element ) ;
31228             var oFragment = oRange.createContextualFragment( html );
31229             element.parentNode.insertBefore( oFragment, element ) ;
31230         }
31231     }
31232     
31233     
31234   
31235     
31236     
31237     
31238     
31239
31240 });
31241
31242 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31243
31244 function FCKeditor_OnComplete(editorInstance){
31245     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31246     f.fckEditor = editorInstance;
31247     //console.log("loaded");
31248     f.fireEvent('editorinit', f, editorInstance);
31249
31250   
31251
31252  
31253
31254
31255
31256
31257
31258
31259
31260
31261
31262
31263
31264
31265
31266
31267
31268 //<script type="text/javascript">
31269 /**
31270  * @class Roo.form.GridField
31271  * @extends Roo.form.Field
31272  * Embed a grid (or editable grid into a form)
31273  * STATUS ALPHA
31274  * 
31275  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31276  * it needs 
31277  * xgrid.store = Roo.data.Store
31278  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31279  * xgrid.store.reader = Roo.data.JsonReader 
31280  * 
31281  * 
31282  * @constructor
31283  * Creates a new GridField
31284  * @param {Object} config Configuration options
31285  */
31286 Roo.form.GridField = function(config){
31287     Roo.form.GridField.superclass.constructor.call(this, config);
31288      
31289 };
31290
31291 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
31292     /**
31293      * @cfg {Number} width  - used to restrict width of grid..
31294      */
31295     width : 100,
31296     /**
31297      * @cfg {Number} height - used to restrict height of grid..
31298      */
31299     height : 50,
31300      /**
31301      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31302          * 
31303          *}
31304      */
31305     xgrid : false, 
31306     /**
31307      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31308      * {tag: "input", type: "checkbox", autocomplete: "off"})
31309      */
31310    // defaultAutoCreate : { tag: 'div' },
31311     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31312     /**
31313      * @cfg {String} addTitle Text to include for adding a title.
31314      */
31315     addTitle : false,
31316     //
31317     onResize : function(){
31318         Roo.form.Field.superclass.onResize.apply(this, arguments);
31319     },
31320
31321     initEvents : function(){
31322         // Roo.form.Checkbox.superclass.initEvents.call(this);
31323         // has no events...
31324        
31325     },
31326
31327
31328     getResizeEl : function(){
31329         return this.wrap;
31330     },
31331
31332     getPositionEl : function(){
31333         return this.wrap;
31334     },
31335
31336     // private
31337     onRender : function(ct, position){
31338         
31339         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31340         var style = this.style;
31341         delete this.style;
31342         
31343         Roo.form.GridField.superclass.onRender.call(this, ct, position);
31344         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31345         this.viewEl = this.wrap.createChild({ tag: 'div' });
31346         if (style) {
31347             this.viewEl.applyStyles(style);
31348         }
31349         if (this.width) {
31350             this.viewEl.setWidth(this.width);
31351         }
31352         if (this.height) {
31353             this.viewEl.setHeight(this.height);
31354         }
31355         //if(this.inputValue !== undefined){
31356         //this.setValue(this.value);
31357         
31358         
31359         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31360         
31361         
31362         this.grid.render();
31363         this.grid.getDataSource().on('remove', this.refreshValue, this);
31364         this.grid.getDataSource().on('update', this.refreshValue, this);
31365         this.grid.on('afteredit', this.refreshValue, this);
31366  
31367     },
31368      
31369     
31370     /**
31371      * Sets the value of the item. 
31372      * @param {String} either an object  or a string..
31373      */
31374     setValue : function(v){
31375         //this.value = v;
31376         v = v || []; // empty set..
31377         // this does not seem smart - it really only affects memoryproxy grids..
31378         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31379             var ds = this.grid.getDataSource();
31380             // assumes a json reader..
31381             var data = {}
31382             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
31383             ds.loadData( data);
31384         }
31385         // clear selection so it does not get stale.
31386         if (this.grid.sm) { 
31387             this.grid.sm.clearSelections();
31388         }
31389         
31390         Roo.form.GridField.superclass.setValue.call(this, v);
31391         this.refreshValue();
31392         // should load data in the grid really....
31393     },
31394     
31395     // private
31396     refreshValue: function() {
31397          var val = [];
31398         this.grid.getDataSource().each(function(r) {
31399             val.push(r.data);
31400         });
31401         this.el.dom.value = Roo.encode(val);
31402     }
31403     
31404      
31405     
31406     
31407 });/*
31408  * Based on:
31409  * Ext JS Library 1.1.1
31410  * Copyright(c) 2006-2007, Ext JS, LLC.
31411  *
31412  * Originally Released Under LGPL - original licence link has changed is not relivant.
31413  *
31414  * Fork - LGPL
31415  * <script type="text/javascript">
31416  */
31417 /**
31418  * @class Roo.form.DisplayField
31419  * @extends Roo.form.Field
31420  * A generic Field to display non-editable data.
31421  * @cfg {Boolean} closable (true|false) default false
31422  * @constructor
31423  * Creates a new Display Field item.
31424  * @param {Object} config Configuration options
31425  */
31426 Roo.form.DisplayField = function(config){
31427     Roo.form.DisplayField.superclass.constructor.call(this, config);
31428     
31429     this.addEvents({
31430         /**
31431          * @event close
31432          * Fires after the click the close btn
31433              * @param {Roo.form.DisplayField} this
31434              */
31435         close : true
31436     });
31437 };
31438
31439 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
31440     inputType:      'hidden',
31441     allowBlank:     true,
31442     readOnly:         true,
31443     
31444  
31445     /**
31446      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31447      */
31448     focusClass : undefined,
31449     /**
31450      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31451      */
31452     fieldClass: 'x-form-field',
31453     
31454      /**
31455      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31456      */
31457     valueRenderer: undefined,
31458     
31459     width: 100,
31460     /**
31461      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31462      * {tag: "input", type: "checkbox", autocomplete: "off"})
31463      */
31464      
31465  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31466  
31467     closable : false,
31468     
31469     onResize : function(){
31470         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31471         
31472     },
31473
31474     initEvents : function(){
31475         // Roo.form.Checkbox.superclass.initEvents.call(this);
31476         // has no events...
31477         
31478         if(this.closable){
31479             this.closeEl.on('click', this.onClose, this);
31480         }
31481        
31482     },
31483
31484
31485     getResizeEl : function(){
31486         return this.wrap;
31487     },
31488
31489     getPositionEl : function(){
31490         return this.wrap;
31491     },
31492
31493     // private
31494     onRender : function(ct, position){
31495         
31496         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31497         //if(this.inputValue !== undefined){
31498         this.wrap = this.el.wrap();
31499         
31500         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31501         
31502         if(this.closable){
31503             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31504         }
31505         
31506         if (this.bodyStyle) {
31507             this.viewEl.applyStyles(this.bodyStyle);
31508         }
31509         //this.viewEl.setStyle('padding', '2px');
31510         
31511         this.setValue(this.value);
31512         
31513     },
31514 /*
31515     // private
31516     initValue : Roo.emptyFn,
31517
31518   */
31519
31520         // private
31521     onClick : function(){
31522         
31523     },
31524
31525     /**
31526      * Sets the checked state of the checkbox.
31527      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31528      */
31529     setValue : function(v){
31530         this.value = v;
31531         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31532         // this might be called before we have a dom element..
31533         if (!this.viewEl) {
31534             return;
31535         }
31536         this.viewEl.dom.innerHTML = html;
31537         Roo.form.DisplayField.superclass.setValue.call(this, v);
31538
31539     },
31540     
31541     onClose : function(e)
31542     {
31543         e.preventDefault();
31544         
31545         this.fireEvent('close', this);
31546     }
31547 });/*
31548  * 
31549  * Licence- LGPL
31550  * 
31551  */
31552
31553 /**
31554  * @class Roo.form.DayPicker
31555  * @extends Roo.form.Field
31556  * A Day picker show [M] [T] [W] ....
31557  * @constructor
31558  * Creates a new Day Picker
31559  * @param {Object} config Configuration options
31560  */
31561 Roo.form.DayPicker= function(config){
31562     Roo.form.DayPicker.superclass.constructor.call(this, config);
31563      
31564 };
31565
31566 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31567     /**
31568      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31569      */
31570     focusClass : undefined,
31571     /**
31572      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31573      */
31574     fieldClass: "x-form-field",
31575    
31576     /**
31577      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31578      * {tag: "input", type: "checkbox", autocomplete: "off"})
31579      */
31580     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31581     
31582    
31583     actionMode : 'viewEl', 
31584     //
31585     // private
31586  
31587     inputType : 'hidden',
31588     
31589      
31590     inputElement: false, // real input element?
31591     basedOn: false, // ????
31592     
31593     isFormField: true, // not sure where this is needed!!!!
31594
31595     onResize : function(){
31596         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31597         if(!this.boxLabel){
31598             this.el.alignTo(this.wrap, 'c-c');
31599         }
31600     },
31601
31602     initEvents : function(){
31603         Roo.form.Checkbox.superclass.initEvents.call(this);
31604         this.el.on("click", this.onClick,  this);
31605         this.el.on("change", this.onClick,  this);
31606     },
31607
31608
31609     getResizeEl : function(){
31610         return this.wrap;
31611     },
31612
31613     getPositionEl : function(){
31614         return this.wrap;
31615     },
31616
31617     
31618     // private
31619     onRender : function(ct, position){
31620         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31621        
31622         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31623         
31624         var r1 = '<table><tr>';
31625         var r2 = '<tr class="x-form-daypick-icons">';
31626         for (var i=0; i < 7; i++) {
31627             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31628             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31629         }
31630         
31631         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31632         viewEl.select('img').on('click', this.onClick, this);
31633         this.viewEl = viewEl;   
31634         
31635         
31636         // this will not work on Chrome!!!
31637         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31638         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31639         
31640         
31641           
31642
31643     },
31644
31645     // private
31646     initValue : Roo.emptyFn,
31647
31648     /**
31649      * Returns the checked state of the checkbox.
31650      * @return {Boolean} True if checked, else false
31651      */
31652     getValue : function(){
31653         return this.el.dom.value;
31654         
31655     },
31656
31657         // private
31658     onClick : function(e){ 
31659         //this.setChecked(!this.checked);
31660         Roo.get(e.target).toggleClass('x-menu-item-checked');
31661         this.refreshValue();
31662         //if(this.el.dom.checked != this.checked){
31663         //    this.setValue(this.el.dom.checked);
31664        // }
31665     },
31666     
31667     // private
31668     refreshValue : function()
31669     {
31670         var val = '';
31671         this.viewEl.select('img',true).each(function(e,i,n)  {
31672             val += e.is(".x-menu-item-checked") ? String(n) : '';
31673         });
31674         this.setValue(val, true);
31675     },
31676
31677     /**
31678      * Sets the checked state of the checkbox.
31679      * On is always based on a string comparison between inputValue and the param.
31680      * @param {Boolean/String} value - the value to set 
31681      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31682      */
31683     setValue : function(v,suppressEvent){
31684         if (!this.el.dom) {
31685             return;
31686         }
31687         var old = this.el.dom.value ;
31688         this.el.dom.value = v;
31689         if (suppressEvent) {
31690             return ;
31691         }
31692          
31693         // update display..
31694         this.viewEl.select('img',true).each(function(e,i,n)  {
31695             
31696             var on = e.is(".x-menu-item-checked");
31697             var newv = v.indexOf(String(n)) > -1;
31698             if (on != newv) {
31699                 e.toggleClass('x-menu-item-checked');
31700             }
31701             
31702         });
31703         
31704         
31705         this.fireEvent('change', this, v, old);
31706         
31707         
31708     },
31709    
31710     // handle setting of hidden value by some other method!!?!?
31711     setFromHidden: function()
31712     {
31713         if(!this.el){
31714             return;
31715         }
31716         //console.log("SET FROM HIDDEN");
31717         //alert('setFrom hidden');
31718         this.setValue(this.el.dom.value);
31719     },
31720     
31721     onDestroy : function()
31722     {
31723         if(this.viewEl){
31724             Roo.get(this.viewEl).remove();
31725         }
31726          
31727         Roo.form.DayPicker.superclass.onDestroy.call(this);
31728     }
31729
31730 });/*
31731  * RooJS Library 1.1.1
31732  * Copyright(c) 2008-2011  Alan Knowles
31733  *
31734  * License - LGPL
31735  */
31736  
31737
31738 /**
31739  * @class Roo.form.ComboCheck
31740  * @extends Roo.form.ComboBox
31741  * A combobox for multiple select items.
31742  *
31743  * FIXME - could do with a reset button..
31744  * 
31745  * @constructor
31746  * Create a new ComboCheck
31747  * @param {Object} config Configuration options
31748  */
31749 Roo.form.ComboCheck = function(config){
31750     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31751     // should verify some data...
31752     // like
31753     // hiddenName = required..
31754     // displayField = required
31755     // valudField == required
31756     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31757     var _t = this;
31758     Roo.each(req, function(e) {
31759         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31760             throw "Roo.form.ComboCheck : missing value for: " + e;
31761         }
31762     });
31763     
31764     
31765 };
31766
31767 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31768      
31769      
31770     editable : false,
31771      
31772     selectedClass: 'x-menu-item-checked', 
31773     
31774     // private
31775     onRender : function(ct, position){
31776         var _t = this;
31777         
31778         
31779         
31780         if(!this.tpl){
31781             var cls = 'x-combo-list';
31782
31783             
31784             this.tpl =  new Roo.Template({
31785                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31786                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31787                    '<span>{' + this.displayField + '}</span>' +
31788                     '</div>' 
31789                 
31790             });
31791         }
31792  
31793         
31794         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31795         this.view.singleSelect = false;
31796         this.view.multiSelect = true;
31797         this.view.toggleSelect = true;
31798         this.pageTb.add(new Roo.Toolbar.Fill(), {
31799             
31800             text: 'Done',
31801             handler: function()
31802             {
31803                 _t.collapse();
31804             }
31805         });
31806     },
31807     
31808     onViewOver : function(e, t){
31809         // do nothing...
31810         return;
31811         
31812     },
31813     
31814     onViewClick : function(doFocus,index){
31815         return;
31816         
31817     },
31818     select: function () {
31819         //Roo.log("SELECT CALLED");
31820     },
31821      
31822     selectByValue : function(xv, scrollIntoView){
31823         var ar = this.getValueArray();
31824         var sels = [];
31825         
31826         Roo.each(ar, function(v) {
31827             if(v === undefined || v === null){
31828                 return;
31829             }
31830             var r = this.findRecord(this.valueField, v);
31831             if(r){
31832                 sels.push(this.store.indexOf(r))
31833                 
31834             }
31835         },this);
31836         this.view.select(sels);
31837         return false;
31838     },
31839     
31840     
31841     
31842     onSelect : function(record, index){
31843        // Roo.log("onselect Called");
31844        // this is only called by the clear button now..
31845         this.view.clearSelections();
31846         this.setValue('[]');
31847         if (this.value != this.valueBefore) {
31848             this.fireEvent('change', this, this.value, this.valueBefore);
31849             this.valueBefore = this.value;
31850         }
31851     },
31852     getValueArray : function()
31853     {
31854         var ar = [] ;
31855         
31856         try {
31857             //Roo.log(this.value);
31858             if (typeof(this.value) == 'undefined') {
31859                 return [];
31860             }
31861             var ar = Roo.decode(this.value);
31862             return  ar instanceof Array ? ar : []; //?? valid?
31863             
31864         } catch(e) {
31865             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31866             return [];
31867         }
31868          
31869     },
31870     expand : function ()
31871     {
31872         
31873         Roo.form.ComboCheck.superclass.expand.call(this);
31874         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31875         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31876         
31877
31878     },
31879     
31880     collapse : function(){
31881         Roo.form.ComboCheck.superclass.collapse.call(this);
31882         var sl = this.view.getSelectedIndexes();
31883         var st = this.store;
31884         var nv = [];
31885         var tv = [];
31886         var r;
31887         Roo.each(sl, function(i) {
31888             r = st.getAt(i);
31889             nv.push(r.get(this.valueField));
31890         },this);
31891         this.setValue(Roo.encode(nv));
31892         if (this.value != this.valueBefore) {
31893
31894             this.fireEvent('change', this, this.value, this.valueBefore);
31895             this.valueBefore = this.value;
31896         }
31897         
31898     },
31899     
31900     setValue : function(v){
31901         // Roo.log(v);
31902         this.value = v;
31903         
31904         var vals = this.getValueArray();
31905         var tv = [];
31906         Roo.each(vals, function(k) {
31907             var r = this.findRecord(this.valueField, k);
31908             if(r){
31909                 tv.push(r.data[this.displayField]);
31910             }else if(this.valueNotFoundText !== undefined){
31911                 tv.push( this.valueNotFoundText );
31912             }
31913         },this);
31914        // Roo.log(tv);
31915         
31916         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31917         this.hiddenField.value = v;
31918         this.value = v;
31919     }
31920     
31921 });/*
31922  * Based on:
31923  * Ext JS Library 1.1.1
31924  * Copyright(c) 2006-2007, Ext JS, LLC.
31925  *
31926  * Originally Released Under LGPL - original licence link has changed is not relivant.
31927  *
31928  * Fork - LGPL
31929  * <script type="text/javascript">
31930  */
31931  
31932 /**
31933  * @class Roo.form.Signature
31934  * @extends Roo.form.Field
31935  * Signature field.  
31936  * @constructor
31937  * 
31938  * @param {Object} config Configuration options
31939  */
31940
31941 Roo.form.Signature = function(config){
31942     Roo.form.Signature.superclass.constructor.call(this, config);
31943     
31944     this.addEvents({// not in used??
31945          /**
31946          * @event confirm
31947          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31948              * @param {Roo.form.Signature} combo This combo box
31949              */
31950         'confirm' : true,
31951         /**
31952          * @event reset
31953          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31954              * @param {Roo.form.ComboBox} combo This combo box
31955              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31956              */
31957         'reset' : true
31958     });
31959 };
31960
31961 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31962     /**
31963      * @cfg {Object} labels Label to use when rendering a form.
31964      * defaults to 
31965      * labels : { 
31966      *      clear : "Clear",
31967      *      confirm : "Confirm"
31968      *  }
31969      */
31970     labels : { 
31971         clear : "Clear",
31972         confirm : "Confirm"
31973     },
31974     /**
31975      * @cfg {Number} width The signature panel width (defaults to 300)
31976      */
31977     width: 300,
31978     /**
31979      * @cfg {Number} height The signature panel height (defaults to 100)
31980      */
31981     height : 100,
31982     /**
31983      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31984      */
31985     allowBlank : false,
31986     
31987     //private
31988     // {Object} signPanel The signature SVG panel element (defaults to {})
31989     signPanel : {},
31990     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31991     isMouseDown : false,
31992     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31993     isConfirmed : false,
31994     // {String} signatureTmp SVG mapping string (defaults to empty string)
31995     signatureTmp : '',
31996     
31997     
31998     defaultAutoCreate : { // modified by initCompnoent..
31999         tag: "input",
32000         type:"hidden"
32001     },
32002
32003     // private
32004     onRender : function(ct, position){
32005         
32006         Roo.form.Signature.superclass.onRender.call(this, ct, position);
32007         
32008         this.wrap = this.el.wrap({
32009             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32010         });
32011         
32012         this.createToolbar(this);
32013         this.signPanel = this.wrap.createChild({
32014                 tag: 'div',
32015                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32016             }, this.el
32017         );
32018             
32019         this.svgID = Roo.id();
32020         this.svgEl = this.signPanel.createChild({
32021               xmlns : 'http://www.w3.org/2000/svg',
32022               tag : 'svg',
32023               id : this.svgID + "-svg",
32024               width: this.width,
32025               height: this.height,
32026               viewBox: '0 0 '+this.width+' '+this.height,
32027               cn : [
32028                 {
32029                     tag: "rect",
32030                     id: this.svgID + "-svg-r",
32031                     width: this.width,
32032                     height: this.height,
32033                     fill: "#ffa"
32034                 },
32035                 {
32036                     tag: "line",
32037                     id: this.svgID + "-svg-l",
32038                     x1: "0", // start
32039                     y1: (this.height*0.8), // start set the line in 80% of height
32040                     x2: this.width, // end
32041                     y2: (this.height*0.8), // end set the line in 80% of height
32042                     'stroke': "#666",
32043                     'stroke-width': "1",
32044                     'stroke-dasharray': "3",
32045                     'shape-rendering': "crispEdges",
32046                     'pointer-events': "none"
32047                 },
32048                 {
32049                     tag: "path",
32050                     id: this.svgID + "-svg-p",
32051                     'stroke': "navy",
32052                     'stroke-width': "3",
32053                     'fill': "none",
32054                     'pointer-events': 'none'
32055                 }
32056               ]
32057         });
32058         this.createSVG();
32059         this.svgBox = this.svgEl.dom.getScreenCTM();
32060     },
32061     createSVG : function(){ 
32062         var svg = this.signPanel;
32063         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32064         var t = this;
32065
32066         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32067         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32068         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32069         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32070         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32071         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32072         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32073         
32074     },
32075     isTouchEvent : function(e){
32076         return e.type.match(/^touch/);
32077     },
32078     getCoords : function (e) {
32079         var pt    = this.svgEl.dom.createSVGPoint();
32080         pt.x = e.clientX; 
32081         pt.y = e.clientY;
32082         if (this.isTouchEvent(e)) {
32083             pt.x =  e.targetTouches[0].clientX;
32084             pt.y = e.targetTouches[0].clientY;
32085         }
32086         var a = this.svgEl.dom.getScreenCTM();
32087         var b = a.inverse();
32088         var mx = pt.matrixTransform(b);
32089         return mx.x + ',' + mx.y;
32090     },
32091     //mouse event headler 
32092     down : function (e) {
32093         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32094         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32095         
32096         this.isMouseDown = true;
32097         
32098         e.preventDefault();
32099     },
32100     move : function (e) {
32101         if (this.isMouseDown) {
32102             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32103             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32104         }
32105         
32106         e.preventDefault();
32107     },
32108     up : function (e) {
32109         this.isMouseDown = false;
32110         var sp = this.signatureTmp.split(' ');
32111         
32112         if(sp.length > 1){
32113             if(!sp[sp.length-2].match(/^L/)){
32114                 sp.pop();
32115                 sp.pop();
32116                 sp.push("");
32117                 this.signatureTmp = sp.join(" ");
32118             }
32119         }
32120         if(this.getValue() != this.signatureTmp){
32121             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32122             this.isConfirmed = false;
32123         }
32124         e.preventDefault();
32125     },
32126     
32127     /**
32128      * Protected method that will not generally be called directly. It
32129      * is called when the editor creates its toolbar. Override this method if you need to
32130      * add custom toolbar buttons.
32131      * @param {HtmlEditor} editor
32132      */
32133     createToolbar : function(editor){
32134          function btn(id, toggle, handler){
32135             var xid = fid + '-'+ id ;
32136             return {
32137                 id : xid,
32138                 cmd : id,
32139                 cls : 'x-btn-icon x-edit-'+id,
32140                 enableToggle:toggle !== false,
32141                 scope: editor, // was editor...
32142                 handler:handler||editor.relayBtnCmd,
32143                 clickEvent:'mousedown',
32144                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32145                 tabIndex:-1
32146             };
32147         }
32148         
32149         
32150         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32151         this.tb = tb;
32152         this.tb.add(
32153            {
32154                 cls : ' x-signature-btn x-signature-'+id,
32155                 scope: editor, // was editor...
32156                 handler: this.reset,
32157                 clickEvent:'mousedown',
32158                 text: this.labels.clear
32159             },
32160             {
32161                  xtype : 'Fill',
32162                  xns: Roo.Toolbar
32163             }, 
32164             {
32165                 cls : '  x-signature-btn x-signature-'+id,
32166                 scope: editor, // was editor...
32167                 handler: this.confirmHandler,
32168                 clickEvent:'mousedown',
32169                 text: this.labels.confirm
32170             }
32171         );
32172     
32173     },
32174     //public
32175     /**
32176      * when user is clicked confirm then show this image.....
32177      * 
32178      * @return {String} Image Data URI
32179      */
32180     getImageDataURI : function(){
32181         var svg = this.svgEl.dom.parentNode.innerHTML;
32182         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32183         return src; 
32184     },
32185     /**
32186      * 
32187      * @return {Boolean} this.isConfirmed
32188      */
32189     getConfirmed : function(){
32190         return this.isConfirmed;
32191     },
32192     /**
32193      * 
32194      * @return {Number} this.width
32195      */
32196     getWidth : function(){
32197         return this.width;
32198     },
32199     /**
32200      * 
32201      * @return {Number} this.height
32202      */
32203     getHeight : function(){
32204         return this.height;
32205     },
32206     // private
32207     getSignature : function(){
32208         return this.signatureTmp;
32209     },
32210     // private
32211     reset : function(){
32212         this.signatureTmp = '';
32213         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32214         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32215         this.isConfirmed = false;
32216         Roo.form.Signature.superclass.reset.call(this);
32217     },
32218     setSignature : function(s){
32219         this.signatureTmp = s;
32220         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32221         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32222         this.setValue(s);
32223         this.isConfirmed = false;
32224         Roo.form.Signature.superclass.reset.call(this);
32225     }, 
32226     test : function(){
32227 //        Roo.log(this.signPanel.dom.contentWindow.up())
32228     },
32229     //private
32230     setConfirmed : function(){
32231         
32232         
32233         
32234 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32235     },
32236     // private
32237     confirmHandler : function(){
32238         if(!this.getSignature()){
32239             return;
32240         }
32241         
32242         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32243         this.setValue(this.getSignature());
32244         this.isConfirmed = true;
32245         
32246         this.fireEvent('confirm', this);
32247     },
32248     // private
32249     // Subclasses should provide the validation implementation by overriding this
32250     validateValue : function(value){
32251         if(this.allowBlank){
32252             return true;
32253         }
32254         
32255         if(this.isConfirmed){
32256             return true;
32257         }
32258         return false;
32259     }
32260 });/*
32261  * Based on:
32262  * Ext JS Library 1.1.1
32263  * Copyright(c) 2006-2007, Ext JS, LLC.
32264  *
32265  * Originally Released Under LGPL - original licence link has changed is not relivant.
32266  *
32267  * Fork - LGPL
32268  * <script type="text/javascript">
32269  */
32270  
32271
32272 /**
32273  * @class Roo.form.ComboBox
32274  * @extends Roo.form.TriggerField
32275  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32276  * @constructor
32277  * Create a new ComboBox.
32278  * @param {Object} config Configuration options
32279  */
32280 Roo.form.Select = function(config){
32281     Roo.form.Select.superclass.constructor.call(this, config);
32282      
32283 };
32284
32285 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32286     /**
32287      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32288      */
32289     /**
32290      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32291      * rendering into an Roo.Editor, defaults to false)
32292      */
32293     /**
32294      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32295      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32296      */
32297     /**
32298      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32299      */
32300     /**
32301      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32302      * the dropdown list (defaults to undefined, with no header element)
32303      */
32304
32305      /**
32306      * @cfg {String/Roo.Template} tpl The template to use to render the output
32307      */
32308      
32309     // private
32310     defaultAutoCreate : {tag: "select"  },
32311     /**
32312      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32313      */
32314     listWidth: undefined,
32315     /**
32316      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32317      * mode = 'remote' or 'text' if mode = 'local')
32318      */
32319     displayField: undefined,
32320     /**
32321      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32322      * mode = 'remote' or 'value' if mode = 'local'). 
32323      * Note: use of a valueField requires the user make a selection
32324      * in order for a value to be mapped.
32325      */
32326     valueField: undefined,
32327     
32328     
32329     /**
32330      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32331      * field's data value (defaults to the underlying DOM element's name)
32332      */
32333     hiddenName: undefined,
32334     /**
32335      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32336      */
32337     listClass: '',
32338     /**
32339      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32340      */
32341     selectedClass: 'x-combo-selected',
32342     /**
32343      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
32344      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32345      * which displays a downward arrow icon).
32346      */
32347     triggerClass : 'x-form-arrow-trigger',
32348     /**
32349      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32350      */
32351     shadow:'sides',
32352     /**
32353      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32354      * anchor positions (defaults to 'tl-bl')
32355      */
32356     listAlign: 'tl-bl?',
32357     /**
32358      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32359      */
32360     maxHeight: 300,
32361     /**
32362      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
32363      * query specified by the allQuery config option (defaults to 'query')
32364      */
32365     triggerAction: 'query',
32366     /**
32367      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32368      * (defaults to 4, does not apply if editable = false)
32369      */
32370     minChars : 4,
32371     /**
32372      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32373      * delay (typeAheadDelay) if it matches a known value (defaults to false)
32374      */
32375     typeAhead: false,
32376     /**
32377      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32378      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32379      */
32380     queryDelay: 500,
32381     /**
32382      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32383      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
32384      */
32385     pageSize: 0,
32386     /**
32387      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
32388      * when editable = true (defaults to false)
32389      */
32390     selectOnFocus:false,
32391     /**
32392      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32393      */
32394     queryParam: 'query',
32395     /**
32396      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
32397      * when mode = 'remote' (defaults to 'Loading...')
32398      */
32399     loadingText: 'Loading...',
32400     /**
32401      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32402      */
32403     resizable: false,
32404     /**
32405      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32406      */
32407     handleHeight : 8,
32408     /**
32409      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32410      * traditional select (defaults to true)
32411      */
32412     editable: true,
32413     /**
32414      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32415      */
32416     allQuery: '',
32417     /**
32418      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32419      */
32420     mode: 'remote',
32421     /**
32422      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32423      * listWidth has a higher value)
32424      */
32425     minListWidth : 70,
32426     /**
32427      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32428      * allow the user to set arbitrary text into the field (defaults to false)
32429      */
32430     forceSelection:false,
32431     /**
32432      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32433      * if typeAhead = true (defaults to 250)
32434      */
32435     typeAheadDelay : 250,
32436     /**
32437      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32438      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32439      */
32440     valueNotFoundText : undefined,
32441     
32442     /**
32443      * @cfg {String} defaultValue The value displayed after loading the store.
32444      */
32445     defaultValue: '',
32446     
32447     /**
32448      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32449      */
32450     blockFocus : false,
32451     
32452     /**
32453      * @cfg {Boolean} disableClear Disable showing of clear button.
32454      */
32455     disableClear : false,
32456     /**
32457      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
32458      */
32459     alwaysQuery : false,
32460     
32461     //private
32462     addicon : false,
32463     editicon: false,
32464     
32465     // element that contains real text value.. (when hidden is used..)
32466      
32467     // private
32468     onRender : function(ct, position){
32469         Roo.form.Field.prototype.onRender.call(this, ct, position);
32470         
32471         if(this.store){
32472             this.store.on('beforeload', this.onBeforeLoad, this);
32473             this.store.on('load', this.onLoad, this);
32474             this.store.on('loadexception', this.onLoadException, this);
32475             this.store.load({});
32476         }
32477         
32478         
32479         
32480     },
32481
32482     // private
32483     initEvents : function(){
32484         //Roo.form.ComboBox.superclass.initEvents.call(this);
32485  
32486     },
32487
32488     onDestroy : function(){
32489        
32490         if(this.store){
32491             this.store.un('beforeload', this.onBeforeLoad, this);
32492             this.store.un('load', this.onLoad, this);
32493             this.store.un('loadexception', this.onLoadException, this);
32494         }
32495         //Roo.form.ComboBox.superclass.onDestroy.call(this);
32496     },
32497
32498     // private
32499     fireKey : function(e){
32500         if(e.isNavKeyPress() && !this.list.isVisible()){
32501             this.fireEvent("specialkey", this, e);
32502         }
32503     },
32504
32505     // private
32506     onResize: function(w, h){
32507         
32508         return; 
32509     
32510         
32511     },
32512
32513     /**
32514      * Allow or prevent the user from directly editing the field text.  If false is passed,
32515      * the user will only be able to select from the items defined in the dropdown list.  This method
32516      * is the runtime equivalent of setting the 'editable' config option at config time.
32517      * @param {Boolean} value True to allow the user to directly edit the field text
32518      */
32519     setEditable : function(value){
32520          
32521     },
32522
32523     // private
32524     onBeforeLoad : function(){
32525         
32526         Roo.log("Select before load");
32527         return;
32528     
32529         this.innerList.update(this.loadingText ?
32530                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32531         //this.restrictHeight();
32532         this.selectedIndex = -1;
32533     },
32534
32535     // private
32536     onLoad : function(){
32537
32538     
32539         var dom = this.el.dom;
32540         dom.innerHTML = '';
32541          var od = dom.ownerDocument;
32542          
32543         if (this.emptyText) {
32544             var op = od.createElement('option');
32545             op.setAttribute('value', '');
32546             op.innerHTML = String.format('{0}', this.emptyText);
32547             dom.appendChild(op);
32548         }
32549         if(this.store.getCount() > 0){
32550            
32551             var vf = this.valueField;
32552             var df = this.displayField;
32553             this.store.data.each(function(r) {
32554                 // which colmsn to use... testing - cdoe / title..
32555                 var op = od.createElement('option');
32556                 op.setAttribute('value', r.data[vf]);
32557                 op.innerHTML = String.format('{0}', r.data[df]);
32558                 dom.appendChild(op);
32559             });
32560             if (typeof(this.defaultValue != 'undefined')) {
32561                 this.setValue(this.defaultValue);
32562             }
32563             
32564              
32565         }else{
32566             //this.onEmptyResults();
32567         }
32568         //this.el.focus();
32569     },
32570     // private
32571     onLoadException : function()
32572     {
32573         dom.innerHTML = '';
32574             
32575         Roo.log("Select on load exception");
32576         return;
32577     
32578         this.collapse();
32579         Roo.log(this.store.reader.jsonData);
32580         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32581             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32582         }
32583         
32584         
32585     },
32586     // private
32587     onTypeAhead : function(){
32588          
32589     },
32590
32591     // private
32592     onSelect : function(record, index){
32593         Roo.log('on select?');
32594         return;
32595         if(this.fireEvent('beforeselect', this, record, index) !== false){
32596             this.setFromData(index > -1 ? record.data : false);
32597             this.collapse();
32598             this.fireEvent('select', this, record, index);
32599         }
32600     },
32601
32602     /**
32603      * Returns the currently selected field value or empty string if no value is set.
32604      * @return {String} value The selected value
32605      */
32606     getValue : function(){
32607         var dom = this.el.dom;
32608         this.value = dom.options[dom.selectedIndex].value;
32609         return this.value;
32610         
32611     },
32612
32613     /**
32614      * Clears any text/value currently set in the field
32615      */
32616     clearValue : function(){
32617         this.value = '';
32618         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32619         
32620     },
32621
32622     /**
32623      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32624      * will be displayed in the field.  If the value does not match the data value of an existing item,
32625      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32626      * Otherwise the field will be blank (although the value will still be set).
32627      * @param {String} value The value to match
32628      */
32629     setValue : function(v){
32630         var d = this.el.dom;
32631         for (var i =0; i < d.options.length;i++) {
32632             if (v == d.options[i].value) {
32633                 d.selectedIndex = i;
32634                 this.value = v;
32635                 return;
32636             }
32637         }
32638         this.clearValue();
32639     },
32640     /**
32641      * @property {Object} the last set data for the element
32642      */
32643     
32644     lastData : false,
32645     /**
32646      * Sets the value of the field based on a object which is related to the record format for the store.
32647      * @param {Object} value the value to set as. or false on reset?
32648      */
32649     setFromData : function(o){
32650         Roo.log('setfrom data?');
32651          
32652         
32653         
32654     },
32655     // private
32656     reset : function(){
32657         this.clearValue();
32658     },
32659     // private
32660     findRecord : function(prop, value){
32661         
32662         return false;
32663     
32664         var record;
32665         if(this.store.getCount() > 0){
32666             this.store.each(function(r){
32667                 if(r.data[prop] == value){
32668                     record = r;
32669                     return false;
32670                 }
32671                 return true;
32672             });
32673         }
32674         return record;
32675     },
32676     
32677     getName: function()
32678     {
32679         // returns hidden if it's set..
32680         if (!this.rendered) {return ''};
32681         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32682         
32683     },
32684      
32685
32686     
32687
32688     // private
32689     onEmptyResults : function(){
32690         Roo.log('empty results');
32691         //this.collapse();
32692     },
32693
32694     /**
32695      * Returns true if the dropdown list is expanded, else false.
32696      */
32697     isExpanded : function(){
32698         return false;
32699     },
32700
32701     /**
32702      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32703      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32704      * @param {String} value The data value of the item to select
32705      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32706      * selected item if it is not currently in view (defaults to true)
32707      * @return {Boolean} True if the value matched an item in the list, else false
32708      */
32709     selectByValue : function(v, scrollIntoView){
32710         Roo.log('select By Value');
32711         return false;
32712     
32713         if(v !== undefined && v !== null){
32714             var r = this.findRecord(this.valueField || this.displayField, v);
32715             if(r){
32716                 this.select(this.store.indexOf(r), scrollIntoView);
32717                 return true;
32718             }
32719         }
32720         return false;
32721     },
32722
32723     /**
32724      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32725      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32726      * @param {Number} index The zero-based index of the list item to select
32727      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32728      * selected item if it is not currently in view (defaults to true)
32729      */
32730     select : function(index, scrollIntoView){
32731         Roo.log('select ');
32732         return  ;
32733         
32734         this.selectedIndex = index;
32735         this.view.select(index);
32736         if(scrollIntoView !== false){
32737             var el = this.view.getNode(index);
32738             if(el){
32739                 this.innerList.scrollChildIntoView(el, false);
32740             }
32741         }
32742     },
32743
32744       
32745
32746     // private
32747     validateBlur : function(){
32748         
32749         return;
32750         
32751     },
32752
32753     // private
32754     initQuery : function(){
32755         this.doQuery(this.getRawValue());
32756     },
32757
32758     // private
32759     doForce : function(){
32760         if(this.el.dom.value.length > 0){
32761             this.el.dom.value =
32762                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32763              
32764         }
32765     },
32766
32767     /**
32768      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32769      * query allowing the query action to be canceled if needed.
32770      * @param {String} query The SQL query to execute
32771      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32772      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32773      * saved in the current store (defaults to false)
32774      */
32775     doQuery : function(q, forceAll){
32776         
32777         Roo.log('doQuery?');
32778         if(q === undefined || q === null){
32779             q = '';
32780         }
32781         var qe = {
32782             query: q,
32783             forceAll: forceAll,
32784             combo: this,
32785             cancel:false
32786         };
32787         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32788             return false;
32789         }
32790         q = qe.query;
32791         forceAll = qe.forceAll;
32792         if(forceAll === true || (q.length >= this.minChars)){
32793             if(this.lastQuery != q || this.alwaysQuery){
32794                 this.lastQuery = q;
32795                 if(this.mode == 'local'){
32796                     this.selectedIndex = -1;
32797                     if(forceAll){
32798                         this.store.clearFilter();
32799                     }else{
32800                         this.store.filter(this.displayField, q);
32801                     }
32802                     this.onLoad();
32803                 }else{
32804                     this.store.baseParams[this.queryParam] = q;
32805                     this.store.load({
32806                         params: this.getParams(q)
32807                     });
32808                     this.expand();
32809                 }
32810             }else{
32811                 this.selectedIndex = -1;
32812                 this.onLoad();   
32813             }
32814         }
32815     },
32816
32817     // private
32818     getParams : function(q){
32819         var p = {};
32820         //p[this.queryParam] = q;
32821         if(this.pageSize){
32822             p.start = 0;
32823             p.limit = this.pageSize;
32824         }
32825         return p;
32826     },
32827
32828     /**
32829      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32830      */
32831     collapse : function(){
32832         
32833     },
32834
32835     // private
32836     collapseIf : function(e){
32837         
32838     },
32839
32840     /**
32841      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32842      */
32843     expand : function(){
32844         
32845     } ,
32846
32847     // private
32848      
32849
32850     /** 
32851     * @cfg {Boolean} grow 
32852     * @hide 
32853     */
32854     /** 
32855     * @cfg {Number} growMin 
32856     * @hide 
32857     */
32858     /** 
32859     * @cfg {Number} growMax 
32860     * @hide 
32861     */
32862     /**
32863      * @hide
32864      * @method autoSize
32865      */
32866     
32867     setWidth : function()
32868     {
32869         
32870     },
32871     getResizeEl : function(){
32872         return this.el;
32873     }
32874 });//<script type="text/javasscript">
32875  
32876
32877 /**
32878  * @class Roo.DDView
32879  * A DnD enabled version of Roo.View.
32880  * @param {Element/String} container The Element in which to create the View.
32881  * @param {String} tpl The template string used to create the markup for each element of the View
32882  * @param {Object} config The configuration properties. These include all the config options of
32883  * {@link Roo.View} plus some specific to this class.<br>
32884  * <p>
32885  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32886  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32887  * <p>
32888  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32889 .x-view-drag-insert-above {
32890         border-top:1px dotted #3366cc;
32891 }
32892 .x-view-drag-insert-below {
32893         border-bottom:1px dotted #3366cc;
32894 }
32895 </code></pre>
32896  * 
32897  */
32898  
32899 Roo.DDView = function(container, tpl, config) {
32900     Roo.DDView.superclass.constructor.apply(this, arguments);
32901     this.getEl().setStyle("outline", "0px none");
32902     this.getEl().unselectable();
32903     if (this.dragGroup) {
32904         this.setDraggable(this.dragGroup.split(","));
32905     }
32906     if (this.dropGroup) {
32907         this.setDroppable(this.dropGroup.split(","));
32908     }
32909     if (this.deletable) {
32910         this.setDeletable();
32911     }
32912     this.isDirtyFlag = false;
32913         this.addEvents({
32914                 "drop" : true
32915         });
32916 };
32917
32918 Roo.extend(Roo.DDView, Roo.View, {
32919 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32920 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32921 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32922 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32923
32924         isFormField: true,
32925
32926         reset: Roo.emptyFn,
32927         
32928         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32929
32930         validate: function() {
32931                 return true;
32932         },
32933         
32934         destroy: function() {
32935                 this.purgeListeners();
32936                 this.getEl.removeAllListeners();
32937                 this.getEl().remove();
32938                 if (this.dragZone) {
32939                         if (this.dragZone.destroy) {
32940                                 this.dragZone.destroy();
32941                         }
32942                 }
32943                 if (this.dropZone) {
32944                         if (this.dropZone.destroy) {
32945                                 this.dropZone.destroy();
32946                         }
32947                 }
32948         },
32949
32950 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32951         getName: function() {
32952                 return this.name;
32953         },
32954
32955 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32956         setValue: function(v) {
32957                 if (!this.store) {
32958                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32959                 }
32960                 var data = {};
32961                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32962                 this.store.proxy = new Roo.data.MemoryProxy(data);
32963                 this.store.load();
32964         },
32965
32966 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32967         getValue: function() {
32968                 var result = '(';
32969                 this.store.each(function(rec) {
32970                         result += rec.id + ',';
32971                 });
32972                 return result.substr(0, result.length - 1) + ')';
32973         },
32974         
32975         getIds: function() {
32976                 var i = 0, result = new Array(this.store.getCount());
32977                 this.store.each(function(rec) {
32978                         result[i++] = rec.id;
32979                 });
32980                 return result;
32981         },
32982         
32983         isDirty: function() {
32984                 return this.isDirtyFlag;
32985         },
32986
32987 /**
32988  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32989  *      whole Element becomes the target, and this causes the drop gesture to append.
32990  */
32991     getTargetFromEvent : function(e) {
32992                 var target = e.getTarget();
32993                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32994                 target = target.parentNode;
32995                 }
32996                 if (!target) {
32997                         target = this.el.dom.lastChild || this.el.dom;
32998                 }
32999                 return target;
33000     },
33001
33002 /**
33003  *      Create the drag data which consists of an object which has the property "ddel" as
33004  *      the drag proxy element. 
33005  */
33006     getDragData : function(e) {
33007         var target = this.findItemFromChild(e.getTarget());
33008                 if(target) {
33009                         this.handleSelection(e);
33010                         var selNodes = this.getSelectedNodes();
33011             var dragData = {
33012                 source: this,
33013                 copy: this.copy || (this.allowCopy && e.ctrlKey),
33014                 nodes: selNodes,
33015                 records: []
33016                         };
33017                         var selectedIndices = this.getSelectedIndexes();
33018                         for (var i = 0; i < selectedIndices.length; i++) {
33019                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
33020                         }
33021                         if (selNodes.length == 1) {
33022                                 dragData.ddel = target.cloneNode(true); // the div element
33023                         } else {
33024                                 var div = document.createElement('div'); // create the multi element drag "ghost"
33025                                 div.className = 'multi-proxy';
33026                                 for (var i = 0, len = selNodes.length; i < len; i++) {
33027                                         div.appendChild(selNodes[i].cloneNode(true));
33028                                 }
33029                                 dragData.ddel = div;
33030                         }
33031             //console.log(dragData)
33032             //console.log(dragData.ddel.innerHTML)
33033                         return dragData;
33034                 }
33035         //console.log('nodragData')
33036                 return false;
33037     },
33038     
33039 /**     Specify to which ddGroup items in this DDView may be dragged. */
33040     setDraggable: function(ddGroup) {
33041         if (ddGroup instanceof Array) {
33042                 Roo.each(ddGroup, this.setDraggable, this);
33043                 return;
33044         }
33045         if (this.dragZone) {
33046                 this.dragZone.addToGroup(ddGroup);
33047         } else {
33048                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33049                                 containerScroll: true,
33050                                 ddGroup: ddGroup 
33051
33052                         });
33053 //                      Draggability implies selection. DragZone's mousedown selects the element.
33054                         if (!this.multiSelect) { this.singleSelect = true; }
33055
33056 //                      Wire the DragZone's handlers up to methods in *this*
33057                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
33058                 }
33059     },
33060
33061 /**     Specify from which ddGroup this DDView accepts drops. */
33062     setDroppable: function(ddGroup) {
33063         if (ddGroup instanceof Array) {
33064                 Roo.each(ddGroup, this.setDroppable, this);
33065                 return;
33066         }
33067         if (this.dropZone) {
33068                 this.dropZone.addToGroup(ddGroup);
33069         } else {
33070                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33071                                 containerScroll: true,
33072                                 ddGroup: ddGroup
33073                         });
33074
33075 //                      Wire the DropZone's handlers up to methods in *this*
33076                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33077                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33078                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33079                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33080                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33081                 }
33082     },
33083
33084 /**     Decide whether to drop above or below a View node. */
33085     getDropPoint : function(e, n, dd){
33086         if (n == this.el.dom) { return "above"; }
33087                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33088                 var c = t + (b - t) / 2;
33089                 var y = Roo.lib.Event.getPageY(e);
33090                 if(y <= c) {
33091                         return "above";
33092                 }else{
33093                         return "below";
33094                 }
33095     },
33096
33097     onNodeEnter : function(n, dd, e, data){
33098                 return false;
33099     },
33100     
33101     onNodeOver : function(n, dd, e, data){
33102                 var pt = this.getDropPoint(e, n, dd);
33103                 // set the insert point style on the target node
33104                 var dragElClass = this.dropNotAllowed;
33105                 if (pt) {
33106                         var targetElClass;
33107                         if (pt == "above"){
33108                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33109                                 targetElClass = "x-view-drag-insert-above";
33110                         } else {
33111                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33112                                 targetElClass = "x-view-drag-insert-below";
33113                         }
33114                         if (this.lastInsertClass != targetElClass){
33115                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33116                                 this.lastInsertClass = targetElClass;
33117                         }
33118                 }
33119                 return dragElClass;
33120         },
33121
33122     onNodeOut : function(n, dd, e, data){
33123                 this.removeDropIndicators(n);
33124     },
33125
33126     onNodeDrop : function(n, dd, e, data){
33127         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33128                 return false;
33129         }
33130         var pt = this.getDropPoint(e, n, dd);
33131                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33132                 if (pt == "below") { insertAt++; }
33133                 for (var i = 0; i < data.records.length; i++) {
33134                         var r = data.records[i];
33135                         var dup = this.store.getById(r.id);
33136                         if (dup && (dd != this.dragZone)) {
33137                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33138                         } else {
33139                                 if (data.copy) {
33140                                         this.store.insert(insertAt++, r.copy());
33141                                 } else {
33142                                         data.source.isDirtyFlag = true;
33143                                         r.store.remove(r);
33144                                         this.store.insert(insertAt++, r);
33145                                 }
33146                                 this.isDirtyFlag = true;
33147                         }
33148                 }
33149                 this.dragZone.cachedTarget = null;
33150                 return true;
33151     },
33152
33153     removeDropIndicators : function(n){
33154                 if(n){
33155                         Roo.fly(n).removeClass([
33156                                 "x-view-drag-insert-above",
33157                                 "x-view-drag-insert-below"]);
33158                         this.lastInsertClass = "_noclass";
33159                 }
33160     },
33161
33162 /**
33163  *      Utility method. Add a delete option to the DDView's context menu.
33164  *      @param {String} imageUrl The URL of the "delete" icon image.
33165  */
33166         setDeletable: function(imageUrl) {
33167                 if (!this.singleSelect && !this.multiSelect) {
33168                         this.singleSelect = true;
33169                 }
33170                 var c = this.getContextMenu();
33171                 this.contextMenu.on("itemclick", function(item) {
33172                         switch (item.id) {
33173                                 case "delete":
33174                                         this.remove(this.getSelectedIndexes());
33175                                         break;
33176                         }
33177                 }, this);
33178                 this.contextMenu.add({
33179                         icon: imageUrl,
33180                         id: "delete",
33181                         text: 'Delete'
33182                 });
33183         },
33184         
33185 /**     Return the context menu for this DDView. */
33186         getContextMenu: function() {
33187                 if (!this.contextMenu) {
33188 //                      Create the View's context menu
33189                         this.contextMenu = new Roo.menu.Menu({
33190                                 id: this.id + "-contextmenu"
33191                         });
33192                         this.el.on("contextmenu", this.showContextMenu, this);
33193                 }
33194                 return this.contextMenu;
33195         },
33196         
33197         disableContextMenu: function() {
33198                 if (this.contextMenu) {
33199                         this.el.un("contextmenu", this.showContextMenu, this);
33200                 }
33201         },
33202
33203         showContextMenu: function(e, item) {
33204         item = this.findItemFromChild(e.getTarget());
33205                 if (item) {
33206                         e.stopEvent();
33207                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33208                         this.contextMenu.showAt(e.getXY());
33209             }
33210     },
33211
33212 /**
33213  *      Remove {@link Roo.data.Record}s at the specified indices.
33214  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33215  */
33216     remove: function(selectedIndices) {
33217                 selectedIndices = [].concat(selectedIndices);
33218                 for (var i = 0; i < selectedIndices.length; i++) {
33219                         var rec = this.store.getAt(selectedIndices[i]);
33220                         this.store.remove(rec);
33221                 }
33222     },
33223
33224 /**
33225  *      Double click fires the event, but also, if this is draggable, and there is only one other
33226  *      related DropZone, it transfers the selected node.
33227  */
33228     onDblClick : function(e){
33229         var item = this.findItemFromChild(e.getTarget());
33230         if(item){
33231             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33232                 return false;
33233             }
33234             if (this.dragGroup) {
33235                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33236                     while (targets.indexOf(this.dropZone) > -1) {
33237                             targets.remove(this.dropZone);
33238                                 }
33239                     if (targets.length == 1) {
33240                                         this.dragZone.cachedTarget = null;
33241                         var el = Roo.get(targets[0].getEl());
33242                         var box = el.getBox(true);
33243                         targets[0].onNodeDrop(el.dom, {
33244                                 target: el.dom,
33245                                 xy: [box.x, box.y + box.height - 1]
33246                         }, null, this.getDragData(e));
33247                     }
33248                 }
33249         }
33250     },
33251     
33252     handleSelection: function(e) {
33253                 this.dragZone.cachedTarget = null;
33254         var item = this.findItemFromChild(e.getTarget());
33255         if (!item) {
33256                 this.clearSelections(true);
33257                 return;
33258         }
33259                 if (item && (this.multiSelect || this.singleSelect)){
33260                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33261                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33262                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33263                                 this.unselect(item);
33264                         } else {
33265                                 this.select(item, this.multiSelect && e.ctrlKey);
33266                                 this.lastSelection = item;
33267                         }
33268                 }
33269     },
33270
33271     onItemClick : function(item, index, e){
33272                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33273                         return false;
33274                 }
33275                 return true;
33276     },
33277
33278     unselect : function(nodeInfo, suppressEvent){
33279                 var node = this.getNode(nodeInfo);
33280                 if(node && this.isSelected(node)){
33281                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33282                                 Roo.fly(node).removeClass(this.selectedClass);
33283                                 this.selections.remove(node);
33284                                 if(!suppressEvent){
33285                                         this.fireEvent("selectionchange", this, this.selections);
33286                                 }
33287                         }
33288                 }
33289     }
33290 });
33291 /*
33292  * Based on:
33293  * Ext JS Library 1.1.1
33294  * Copyright(c) 2006-2007, Ext JS, LLC.
33295  *
33296  * Originally Released Under LGPL - original licence link has changed is not relivant.
33297  *
33298  * Fork - LGPL
33299  * <script type="text/javascript">
33300  */
33301  
33302 /**
33303  * @class Roo.LayoutManager
33304  * @extends Roo.util.Observable
33305  * Base class for layout managers.
33306  */
33307 Roo.LayoutManager = function(container, config){
33308     Roo.LayoutManager.superclass.constructor.call(this);
33309     this.el = Roo.get(container);
33310     // ie scrollbar fix
33311     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33312         document.body.scroll = "no";
33313     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33314         this.el.position('relative');
33315     }
33316     this.id = this.el.id;
33317     this.el.addClass("x-layout-container");
33318     /** false to disable window resize monitoring @type Boolean */
33319     this.monitorWindowResize = true;
33320     this.regions = {};
33321     this.addEvents({
33322         /**
33323          * @event layout
33324          * Fires when a layout is performed. 
33325          * @param {Roo.LayoutManager} this
33326          */
33327         "layout" : true,
33328         /**
33329          * @event regionresized
33330          * Fires when the user resizes a region. 
33331          * @param {Roo.LayoutRegion} region The resized region
33332          * @param {Number} newSize The new size (width for east/west, height for north/south)
33333          */
33334         "regionresized" : true,
33335         /**
33336          * @event regioncollapsed
33337          * Fires when a region is collapsed. 
33338          * @param {Roo.LayoutRegion} region The collapsed region
33339          */
33340         "regioncollapsed" : true,
33341         /**
33342          * @event regionexpanded
33343          * Fires when a region is expanded.  
33344          * @param {Roo.LayoutRegion} region The expanded region
33345          */
33346         "regionexpanded" : true
33347     });
33348     this.updating = false;
33349     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33350 };
33351
33352 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33353     /**
33354      * Returns true if this layout is currently being updated
33355      * @return {Boolean}
33356      */
33357     isUpdating : function(){
33358         return this.updating; 
33359     },
33360     
33361     /**
33362      * Suspend the LayoutManager from doing auto-layouts while
33363      * making multiple add or remove calls
33364      */
33365     beginUpdate : function(){
33366         this.updating = true;    
33367     },
33368     
33369     /**
33370      * Restore auto-layouts and optionally disable the manager from performing a layout
33371      * @param {Boolean} noLayout true to disable a layout update 
33372      */
33373     endUpdate : function(noLayout){
33374         this.updating = false;
33375         if(!noLayout){
33376             this.layout();
33377         }    
33378     },
33379     
33380     layout: function(){
33381         
33382     },
33383     
33384     onRegionResized : function(region, newSize){
33385         this.fireEvent("regionresized", region, newSize);
33386         this.layout();
33387     },
33388     
33389     onRegionCollapsed : function(region){
33390         this.fireEvent("regioncollapsed", region);
33391     },
33392     
33393     onRegionExpanded : function(region){
33394         this.fireEvent("regionexpanded", region);
33395     },
33396         
33397     /**
33398      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33399      * performs box-model adjustments.
33400      * @return {Object} The size as an object {width: (the width), height: (the height)}
33401      */
33402     getViewSize : function(){
33403         var size;
33404         if(this.el.dom != document.body){
33405             size = this.el.getSize();
33406         }else{
33407             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33408         }
33409         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33410         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33411         return size;
33412     },
33413     
33414     /**
33415      * Returns the Element this layout is bound to.
33416      * @return {Roo.Element}
33417      */
33418     getEl : function(){
33419         return this.el;
33420     },
33421     
33422     /**
33423      * Returns the specified region.
33424      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33425      * @return {Roo.LayoutRegion}
33426      */
33427     getRegion : function(target){
33428         return this.regions[target.toLowerCase()];
33429     },
33430     
33431     onWindowResize : function(){
33432         if(this.monitorWindowResize){
33433             this.layout();
33434         }
33435     }
33436 });/*
33437  * Based on:
33438  * Ext JS Library 1.1.1
33439  * Copyright(c) 2006-2007, Ext JS, LLC.
33440  *
33441  * Originally Released Under LGPL - original licence link has changed is not relivant.
33442  *
33443  * Fork - LGPL
33444  * <script type="text/javascript">
33445  */
33446 /**
33447  * @class Roo.BorderLayout
33448  * @extends Roo.LayoutManager
33449  * @children Roo.ContentPanel
33450  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33451  * please see: <br><br>
33452  * <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>
33453  * <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>
33454  * Example:
33455  <pre><code>
33456  var layout = new Roo.BorderLayout(document.body, {
33457     north: {
33458         initialSize: 25,
33459         titlebar: false
33460     },
33461     west: {
33462         split:true,
33463         initialSize: 200,
33464         minSize: 175,
33465         maxSize: 400,
33466         titlebar: true,
33467         collapsible: true
33468     },
33469     east: {
33470         split:true,
33471         initialSize: 202,
33472         minSize: 175,
33473         maxSize: 400,
33474         titlebar: true,
33475         collapsible: true
33476     },
33477     south: {
33478         split:true,
33479         initialSize: 100,
33480         minSize: 100,
33481         maxSize: 200,
33482         titlebar: true,
33483         collapsible: true
33484     },
33485     center: {
33486         titlebar: true,
33487         autoScroll:true,
33488         resizeTabs: true,
33489         minTabWidth: 50,
33490         preferredTabWidth: 150
33491     }
33492 });
33493
33494 // shorthand
33495 var CP = Roo.ContentPanel;
33496
33497 layout.beginUpdate();
33498 layout.add("north", new CP("north", "North"));
33499 layout.add("south", new CP("south", {title: "South", closable: true}));
33500 layout.add("west", new CP("west", {title: "West"}));
33501 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33502 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33503 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33504 layout.getRegion("center").showPanel("center1");
33505 layout.endUpdate();
33506 </code></pre>
33507
33508 <b>The container the layout is rendered into can be either the body element or any other element.
33509 If it is not the body element, the container needs to either be an absolute positioned element,
33510 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33511 the container size if it is not the body element.</b>
33512
33513 * @constructor
33514 * Create a new BorderLayout
33515 * @param {String/HTMLElement/Element} container The container this layout is bound to
33516 * @param {Object} config Configuration options
33517  */
33518 Roo.BorderLayout = function(container, config){
33519     config = config || {};
33520     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33521     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33522     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33523         var target = this.factory.validRegions[i];
33524         if(config[target]){
33525             this.addRegion(target, config[target]);
33526         }
33527     }
33528 };
33529
33530 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33531         
33532         /**
33533          * @cfg {Roo.LayoutRegion} east
33534          */
33535         /**
33536          * @cfg {Roo.LayoutRegion} west
33537          */
33538         /**
33539          * @cfg {Roo.LayoutRegion} north
33540          */
33541         /**
33542          * @cfg {Roo.LayoutRegion} south
33543          */
33544         /**
33545          * @cfg {Roo.LayoutRegion} center
33546          */
33547     /**
33548      * Creates and adds a new region if it doesn't already exist.
33549      * @param {String} target The target region key (north, south, east, west or center).
33550      * @param {Object} config The regions config object
33551      * @return {BorderLayoutRegion} The new region
33552      */
33553     addRegion : function(target, config){
33554         if(!this.regions[target]){
33555             var r = this.factory.create(target, this, config);
33556             this.bindRegion(target, r);
33557         }
33558         return this.regions[target];
33559     },
33560
33561     // private (kinda)
33562     bindRegion : function(name, r){
33563         this.regions[name] = r;
33564         r.on("visibilitychange", this.layout, this);
33565         r.on("paneladded", this.layout, this);
33566         r.on("panelremoved", this.layout, this);
33567         r.on("invalidated", this.layout, this);
33568         r.on("resized", this.onRegionResized, this);
33569         r.on("collapsed", this.onRegionCollapsed, this);
33570         r.on("expanded", this.onRegionExpanded, this);
33571     },
33572
33573     /**
33574      * Performs a layout update.
33575      */
33576     layout : function(){
33577         if(this.updating) {
33578             return;
33579         }
33580         var size = this.getViewSize();
33581         var w = size.width;
33582         var h = size.height;
33583         var centerW = w;
33584         var centerH = h;
33585         var centerY = 0;
33586         var centerX = 0;
33587         //var x = 0, y = 0;
33588
33589         var rs = this.regions;
33590         var north = rs["north"];
33591         var south = rs["south"]; 
33592         var west = rs["west"];
33593         var east = rs["east"];
33594         var center = rs["center"];
33595         //if(this.hideOnLayout){ // not supported anymore
33596             //c.el.setStyle("display", "none");
33597         //}
33598         if(north && north.isVisible()){
33599             var b = north.getBox();
33600             var m = north.getMargins();
33601             b.width = w - (m.left+m.right);
33602             b.x = m.left;
33603             b.y = m.top;
33604             centerY = b.height + b.y + m.bottom;
33605             centerH -= centerY;
33606             north.updateBox(this.safeBox(b));
33607         }
33608         if(south && south.isVisible()){
33609             var b = south.getBox();
33610             var m = south.getMargins();
33611             b.width = w - (m.left+m.right);
33612             b.x = m.left;
33613             var totalHeight = (b.height + m.top + m.bottom);
33614             b.y = h - totalHeight + m.top;
33615             centerH -= totalHeight;
33616             south.updateBox(this.safeBox(b));
33617         }
33618         if(west && west.isVisible()){
33619             var b = west.getBox();
33620             var m = west.getMargins();
33621             b.height = centerH - (m.top+m.bottom);
33622             b.x = m.left;
33623             b.y = centerY + m.top;
33624             var totalWidth = (b.width + m.left + m.right);
33625             centerX += totalWidth;
33626             centerW -= totalWidth;
33627             west.updateBox(this.safeBox(b));
33628         }
33629         if(east && east.isVisible()){
33630             var b = east.getBox();
33631             var m = east.getMargins();
33632             b.height = centerH - (m.top+m.bottom);
33633             var totalWidth = (b.width + m.left + m.right);
33634             b.x = w - totalWidth + m.left;
33635             b.y = centerY + m.top;
33636             centerW -= totalWidth;
33637             east.updateBox(this.safeBox(b));
33638         }
33639         if(center){
33640             var m = center.getMargins();
33641             var centerBox = {
33642                 x: centerX + m.left,
33643                 y: centerY + m.top,
33644                 width: centerW - (m.left+m.right),
33645                 height: centerH - (m.top+m.bottom)
33646             };
33647             //if(this.hideOnLayout){
33648                 //center.el.setStyle("display", "block");
33649             //}
33650             center.updateBox(this.safeBox(centerBox));
33651         }
33652         this.el.repaint();
33653         this.fireEvent("layout", this);
33654     },
33655
33656     // private
33657     safeBox : function(box){
33658         box.width = Math.max(0, box.width);
33659         box.height = Math.max(0, box.height);
33660         return box;
33661     },
33662
33663     /**
33664      * Adds a ContentPanel (or subclass) to this layout.
33665      * @param {String} target The target region key (north, south, east, west or center).
33666      * @param {Roo.ContentPanel} panel The panel to add
33667      * @return {Roo.ContentPanel} The added panel
33668      */
33669     add : function(target, panel){
33670          
33671         target = target.toLowerCase();
33672         return this.regions[target].add(panel);
33673     },
33674
33675     /**
33676      * Remove a ContentPanel (or subclass) to this layout.
33677      * @param {String} target The target region key (north, south, east, west or center).
33678      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33679      * @return {Roo.ContentPanel} The removed panel
33680      */
33681     remove : function(target, panel){
33682         target = target.toLowerCase();
33683         return this.regions[target].remove(panel);
33684     },
33685
33686     /**
33687      * Searches all regions for a panel with the specified id
33688      * @param {String} panelId
33689      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33690      */
33691     findPanel : function(panelId){
33692         var rs = this.regions;
33693         for(var target in rs){
33694             if(typeof rs[target] != "function"){
33695                 var p = rs[target].getPanel(panelId);
33696                 if(p){
33697                     return p;
33698                 }
33699             }
33700         }
33701         return null;
33702     },
33703
33704     /**
33705      * Searches all regions for a panel with the specified id and activates (shows) it.
33706      * @param {String/ContentPanel} panelId The panels id or the panel itself
33707      * @return {Roo.ContentPanel} The shown panel or null
33708      */
33709     showPanel : function(panelId) {
33710       var rs = this.regions;
33711       for(var target in rs){
33712          var r = rs[target];
33713          if(typeof r != "function"){
33714             if(r.hasPanel(panelId)){
33715                return r.showPanel(panelId);
33716             }
33717          }
33718       }
33719       return null;
33720    },
33721
33722    /**
33723      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33724      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33725      */
33726     restoreState : function(provider){
33727         if(!provider){
33728             provider = Roo.state.Manager;
33729         }
33730         var sm = new Roo.LayoutStateManager();
33731         sm.init(this, provider);
33732     },
33733
33734     /**
33735      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33736      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33737      * a valid ContentPanel config object.  Example:
33738      * <pre><code>
33739 // Create the main layout
33740 var layout = new Roo.BorderLayout('main-ct', {
33741     west: {
33742         split:true,
33743         minSize: 175,
33744         titlebar: true
33745     },
33746     center: {
33747         title:'Components'
33748     }
33749 }, 'main-ct');
33750
33751 // Create and add multiple ContentPanels at once via configs
33752 layout.batchAdd({
33753    west: {
33754        id: 'source-files',
33755        autoCreate:true,
33756        title:'Ext Source Files',
33757        autoScroll:true,
33758        fitToFrame:true
33759    },
33760    center : {
33761        el: cview,
33762        autoScroll:true,
33763        fitToFrame:true,
33764        toolbar: tb,
33765        resizeEl:'cbody'
33766    }
33767 });
33768 </code></pre>
33769      * @param {Object} regions An object containing ContentPanel configs by region name
33770      */
33771     batchAdd : function(regions){
33772         this.beginUpdate();
33773         for(var rname in regions){
33774             var lr = this.regions[rname];
33775             if(lr){
33776                 this.addTypedPanels(lr, regions[rname]);
33777             }
33778         }
33779         this.endUpdate();
33780     },
33781
33782     // private
33783     addTypedPanels : function(lr, ps){
33784         if(typeof ps == 'string'){
33785             lr.add(new Roo.ContentPanel(ps));
33786         }
33787         else if(ps instanceof Array){
33788             for(var i =0, len = ps.length; i < len; i++){
33789                 this.addTypedPanels(lr, ps[i]);
33790             }
33791         }
33792         else if(!ps.events){ // raw config?
33793             var el = ps.el;
33794             delete ps.el; // prevent conflict
33795             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33796         }
33797         else {  // panel object assumed!
33798             lr.add(ps);
33799         }
33800     },
33801     /**
33802      * Adds a xtype elements to the layout.
33803      * <pre><code>
33804
33805 layout.addxtype({
33806        xtype : 'ContentPanel',
33807        region: 'west',
33808        items: [ .... ]
33809    }
33810 );
33811
33812 layout.addxtype({
33813         xtype : 'NestedLayoutPanel',
33814         region: 'west',
33815         layout: {
33816            center: { },
33817            west: { }   
33818         },
33819         items : [ ... list of content panels or nested layout panels.. ]
33820    }
33821 );
33822 </code></pre>
33823      * @param {Object} cfg Xtype definition of item to add.
33824      */
33825     addxtype : function(cfg)
33826     {
33827         // basically accepts a pannel...
33828         // can accept a layout region..!?!?
33829         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33830         
33831         if (!cfg.xtype.match(/Panel$/)) {
33832             return false;
33833         }
33834         var ret = false;
33835         
33836         if (typeof(cfg.region) == 'undefined') {
33837             Roo.log("Failed to add Panel, region was not set");
33838             Roo.log(cfg);
33839             return false;
33840         }
33841         var region = cfg.region;
33842         delete cfg.region;
33843         
33844           
33845         var xitems = [];
33846         if (cfg.items) {
33847             xitems = cfg.items;
33848             delete cfg.items;
33849         }
33850         var nb = false;
33851         
33852         switch(cfg.xtype) 
33853         {
33854             case 'ContentPanel':  // ContentPanel (el, cfg)
33855             case 'ScrollPanel':  // ContentPanel (el, cfg)
33856             case 'ViewPanel': 
33857                 if(cfg.autoCreate) {
33858                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33859                 } else {
33860                     var el = this.el.createChild();
33861                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33862                 }
33863                 
33864                 this.add(region, ret);
33865                 break;
33866             
33867             
33868             case 'TreePanel': // our new panel!
33869                 cfg.el = this.el.createChild();
33870                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33871                 this.add(region, ret);
33872                 break;
33873             
33874             case 'NestedLayoutPanel': 
33875                 // create a new Layout (which is  a Border Layout...
33876                 var el = this.el.createChild();
33877                 var clayout = cfg.layout;
33878                 delete cfg.layout;
33879                 clayout.items   = clayout.items  || [];
33880                 // replace this exitems with the clayout ones..
33881                 xitems = clayout.items;
33882                  
33883                 
33884                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33885                     cfg.background = false;
33886                 }
33887                 var layout = new Roo.BorderLayout(el, clayout);
33888                 
33889                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33890                 //console.log('adding nested layout panel '  + cfg.toSource());
33891                 this.add(region, ret);
33892                 nb = {}; /// find first...
33893                 break;
33894                 
33895             case 'GridPanel': 
33896             
33897                 // needs grid and region
33898                 
33899                 //var el = this.getRegion(region).el.createChild();
33900                 var el = this.el.createChild();
33901                 // create the grid first...
33902                 
33903                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33904                 delete cfg.grid;
33905                 if (region == 'center' && this.active ) {
33906                     cfg.background = false;
33907                 }
33908                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33909                 
33910                 this.add(region, ret);
33911                 if (cfg.background) {
33912                     ret.on('activate', function(gp) {
33913                         if (!gp.grid.rendered) {
33914                             gp.grid.render();
33915                         }
33916                     });
33917                 } else {
33918                     grid.render();
33919                 }
33920                 break;
33921            
33922            
33923            
33924                 
33925                 
33926                 
33927             default:
33928                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33929                     
33930                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33931                     this.add(region, ret);
33932                 } else {
33933                 
33934                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33935                     return null;
33936                 }
33937                 
33938              // GridPanel (grid, cfg)
33939             
33940         }
33941         this.beginUpdate();
33942         // add children..
33943         var region = '';
33944         var abn = {};
33945         Roo.each(xitems, function(i)  {
33946             region = nb && i.region ? i.region : false;
33947             
33948             var add = ret.addxtype(i);
33949            
33950             if (region) {
33951                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33952                 if (!i.background) {
33953                     abn[region] = nb[region] ;
33954                 }
33955             }
33956             
33957         });
33958         this.endUpdate();
33959
33960         // make the last non-background panel active..
33961         //if (nb) { Roo.log(abn); }
33962         if (nb) {
33963             
33964             for(var r in abn) {
33965                 region = this.getRegion(r);
33966                 if (region) {
33967                     // tried using nb[r], but it does not work..
33968                      
33969                     region.showPanel(abn[r]);
33970                    
33971                 }
33972             }
33973         }
33974         return ret;
33975         
33976     }
33977 });
33978
33979 /**
33980  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33981  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33982  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33983  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33984  * <pre><code>
33985 // shorthand
33986 var CP = Roo.ContentPanel;
33987
33988 var layout = Roo.BorderLayout.create({
33989     north: {
33990         initialSize: 25,
33991         titlebar: false,
33992         panels: [new CP("north", "North")]
33993     },
33994     west: {
33995         split:true,
33996         initialSize: 200,
33997         minSize: 175,
33998         maxSize: 400,
33999         titlebar: true,
34000         collapsible: true,
34001         panels: [new CP("west", {title: "West"})]
34002     },
34003     east: {
34004         split:true,
34005         initialSize: 202,
34006         minSize: 175,
34007         maxSize: 400,
34008         titlebar: true,
34009         collapsible: true,
34010         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34011     },
34012     south: {
34013         split:true,
34014         initialSize: 100,
34015         minSize: 100,
34016         maxSize: 200,
34017         titlebar: true,
34018         collapsible: true,
34019         panels: [new CP("south", {title: "South", closable: true})]
34020     },
34021     center: {
34022         titlebar: true,
34023         autoScroll:true,
34024         resizeTabs: true,
34025         minTabWidth: 50,
34026         preferredTabWidth: 150,
34027         panels: [
34028             new CP("center1", {title: "Close Me", closable: true}),
34029             new CP("center2", {title: "Center Panel", closable: false})
34030         ]
34031     }
34032 }, document.body);
34033
34034 layout.getRegion("center").showPanel("center1");
34035 </code></pre>
34036  * @param config
34037  * @param targetEl
34038  */
34039 Roo.BorderLayout.create = function(config, targetEl){
34040     var layout = new Roo.BorderLayout(targetEl || document.body, config);
34041     layout.beginUpdate();
34042     var regions = Roo.BorderLayout.RegionFactory.validRegions;
34043     for(var j = 0, jlen = regions.length; j < jlen; j++){
34044         var lr = regions[j];
34045         if(layout.regions[lr] && config[lr].panels){
34046             var r = layout.regions[lr];
34047             var ps = config[lr].panels;
34048             layout.addTypedPanels(r, ps);
34049         }
34050     }
34051     layout.endUpdate();
34052     return layout;
34053 };
34054
34055 // private
34056 Roo.BorderLayout.RegionFactory = {
34057     // private
34058     validRegions : ["north","south","east","west","center"],
34059
34060     // private
34061     create : function(target, mgr, config){
34062         target = target.toLowerCase();
34063         if(config.lightweight || config.basic){
34064             return new Roo.BasicLayoutRegion(mgr, config, target);
34065         }
34066         switch(target){
34067             case "north":
34068                 return new Roo.NorthLayoutRegion(mgr, config);
34069             case "south":
34070                 return new Roo.SouthLayoutRegion(mgr, config);
34071             case "east":
34072                 return new Roo.EastLayoutRegion(mgr, config);
34073             case "west":
34074                 return new Roo.WestLayoutRegion(mgr, config);
34075             case "center":
34076                 return new Roo.CenterLayoutRegion(mgr, config);
34077         }
34078         throw 'Layout region "'+target+'" not supported.';
34079     }
34080 };/*
34081  * Based on:
34082  * Ext JS Library 1.1.1
34083  * Copyright(c) 2006-2007, Ext JS, LLC.
34084  *
34085  * Originally Released Under LGPL - original licence link has changed is not relivant.
34086  *
34087  * Fork - LGPL
34088  * <script type="text/javascript">
34089  */
34090  
34091 /**
34092  * @class Roo.BasicLayoutRegion
34093  * @extends Roo.util.Observable
34094  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34095  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34096  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34097  */
34098 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34099     this.mgr = mgr;
34100     this.position  = pos;
34101     this.events = {
34102         /**
34103          * @scope Roo.BasicLayoutRegion
34104          */
34105         
34106         /**
34107          * @event beforeremove
34108          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34109          * @param {Roo.LayoutRegion} this
34110          * @param {Roo.ContentPanel} panel The panel
34111          * @param {Object} e The cancel event object
34112          */
34113         "beforeremove" : true,
34114         /**
34115          * @event invalidated
34116          * Fires when the layout for this region is changed.
34117          * @param {Roo.LayoutRegion} this
34118          */
34119         "invalidated" : true,
34120         /**
34121          * @event visibilitychange
34122          * Fires when this region is shown or hidden 
34123          * @param {Roo.LayoutRegion} this
34124          * @param {Boolean} visibility true or false
34125          */
34126         "visibilitychange" : true,
34127         /**
34128          * @event paneladded
34129          * Fires when a panel is added. 
34130          * @param {Roo.LayoutRegion} this
34131          * @param {Roo.ContentPanel} panel The panel
34132          */
34133         "paneladded" : true,
34134         /**
34135          * @event panelremoved
34136          * Fires when a panel is removed. 
34137          * @param {Roo.LayoutRegion} this
34138          * @param {Roo.ContentPanel} panel The panel
34139          */
34140         "panelremoved" : true,
34141         /**
34142          * @event beforecollapse
34143          * Fires when this region before collapse.
34144          * @param {Roo.LayoutRegion} this
34145          */
34146         "beforecollapse" : true,
34147         /**
34148          * @event collapsed
34149          * Fires when this region is collapsed.
34150          * @param {Roo.LayoutRegion} this
34151          */
34152         "collapsed" : true,
34153         /**
34154          * @event expanded
34155          * Fires when this region is expanded.
34156          * @param {Roo.LayoutRegion} this
34157          */
34158         "expanded" : true,
34159         /**
34160          * @event slideshow
34161          * Fires when this region is slid into view.
34162          * @param {Roo.LayoutRegion} this
34163          */
34164         "slideshow" : true,
34165         /**
34166          * @event slidehide
34167          * Fires when this region slides out of view. 
34168          * @param {Roo.LayoutRegion} this
34169          */
34170         "slidehide" : true,
34171         /**
34172          * @event panelactivated
34173          * Fires when a panel is activated. 
34174          * @param {Roo.LayoutRegion} this
34175          * @param {Roo.ContentPanel} panel The activated panel
34176          */
34177         "panelactivated" : true,
34178         /**
34179          * @event resized
34180          * Fires when the user resizes this region. 
34181          * @param {Roo.LayoutRegion} this
34182          * @param {Number} newSize The new size (width for east/west, height for north/south)
34183          */
34184         "resized" : true
34185     };
34186     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34187     this.panels = new Roo.util.MixedCollection();
34188     this.panels.getKey = this.getPanelId.createDelegate(this);
34189     this.box = null;
34190     this.activePanel = null;
34191     // ensure listeners are added...
34192     
34193     if (config.listeners || config.events) {
34194         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34195             listeners : config.listeners || {},
34196             events : config.events || {}
34197         });
34198     }
34199     
34200     if(skipConfig !== true){
34201         this.applyConfig(config);
34202     }
34203 };
34204
34205 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34206     getPanelId : function(p){
34207         return p.getId();
34208     },
34209     
34210     applyConfig : function(config){
34211         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34212         this.config = config;
34213         
34214     },
34215     
34216     /**
34217      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34218      * the width, for horizontal (north, south) the height.
34219      * @param {Number} newSize The new width or height
34220      */
34221     resizeTo : function(newSize){
34222         var el = this.el ? this.el :
34223                  (this.activePanel ? this.activePanel.getEl() : null);
34224         if(el){
34225             switch(this.position){
34226                 case "east":
34227                 case "west":
34228                     el.setWidth(newSize);
34229                     this.fireEvent("resized", this, newSize);
34230                 break;
34231                 case "north":
34232                 case "south":
34233                     el.setHeight(newSize);
34234                     this.fireEvent("resized", this, newSize);
34235                 break;                
34236             }
34237         }
34238     },
34239     
34240     getBox : function(){
34241         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34242     },
34243     
34244     getMargins : function(){
34245         return this.margins;
34246     },
34247     
34248     updateBox : function(box){
34249         this.box = box;
34250         var el = this.activePanel.getEl();
34251         el.dom.style.left = box.x + "px";
34252         el.dom.style.top = box.y + "px";
34253         this.activePanel.setSize(box.width, box.height);
34254     },
34255     
34256     /**
34257      * Returns the container element for this region.
34258      * @return {Roo.Element}
34259      */
34260     getEl : function(){
34261         return this.activePanel;
34262     },
34263     
34264     /**
34265      * Returns true if this region is currently visible.
34266      * @return {Boolean}
34267      */
34268     isVisible : function(){
34269         return this.activePanel ? true : false;
34270     },
34271     
34272     setActivePanel : function(panel){
34273         panel = this.getPanel(panel);
34274         if(this.activePanel && this.activePanel != panel){
34275             this.activePanel.setActiveState(false);
34276             this.activePanel.getEl().setLeftTop(-10000,-10000);
34277         }
34278         this.activePanel = panel;
34279         panel.setActiveState(true);
34280         if(this.box){
34281             panel.setSize(this.box.width, this.box.height);
34282         }
34283         this.fireEvent("panelactivated", this, panel);
34284         this.fireEvent("invalidated");
34285     },
34286     
34287     /**
34288      * Show the specified panel.
34289      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34290      * @return {Roo.ContentPanel} The shown panel or null
34291      */
34292     showPanel : function(panel){
34293         if(panel = this.getPanel(panel)){
34294             this.setActivePanel(panel);
34295         }
34296         return panel;
34297     },
34298     
34299     /**
34300      * Get the active panel for this region.
34301      * @return {Roo.ContentPanel} The active panel or null
34302      */
34303     getActivePanel : function(){
34304         return this.activePanel;
34305     },
34306     
34307     /**
34308      * Add the passed ContentPanel(s)
34309      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34310      * @return {Roo.ContentPanel} The panel added (if only one was added)
34311      */
34312     add : function(panel){
34313         if(arguments.length > 1){
34314             for(var i = 0, len = arguments.length; i < len; i++) {
34315                 this.add(arguments[i]);
34316             }
34317             return null;
34318         }
34319         if(this.hasPanel(panel)){
34320             this.showPanel(panel);
34321             return panel;
34322         }
34323         var el = panel.getEl();
34324         if(el.dom.parentNode != this.mgr.el.dom){
34325             this.mgr.el.dom.appendChild(el.dom);
34326         }
34327         if(panel.setRegion){
34328             panel.setRegion(this);
34329         }
34330         this.panels.add(panel);
34331         el.setStyle("position", "absolute");
34332         if(!panel.background){
34333             this.setActivePanel(panel);
34334             if(this.config.initialSize && this.panels.getCount()==1){
34335                 this.resizeTo(this.config.initialSize);
34336             }
34337         }
34338         this.fireEvent("paneladded", this, panel);
34339         return panel;
34340     },
34341     
34342     /**
34343      * Returns true if the panel is in this region.
34344      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34345      * @return {Boolean}
34346      */
34347     hasPanel : function(panel){
34348         if(typeof panel == "object"){ // must be panel obj
34349             panel = panel.getId();
34350         }
34351         return this.getPanel(panel) ? true : false;
34352     },
34353     
34354     /**
34355      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34356      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34357      * @param {Boolean} preservePanel Overrides the config preservePanel option
34358      * @return {Roo.ContentPanel} The panel that was removed
34359      */
34360     remove : function(panel, preservePanel){
34361         panel = this.getPanel(panel);
34362         if(!panel){
34363             return null;
34364         }
34365         var e = {};
34366         this.fireEvent("beforeremove", this, panel, e);
34367         if(e.cancel === true){
34368             return null;
34369         }
34370         var panelId = panel.getId();
34371         this.panels.removeKey(panelId);
34372         return panel;
34373     },
34374     
34375     /**
34376      * Returns the panel specified or null if it's not in this region.
34377      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34378      * @return {Roo.ContentPanel}
34379      */
34380     getPanel : function(id){
34381         if(typeof id == "object"){ // must be panel obj
34382             return id;
34383         }
34384         return this.panels.get(id);
34385     },
34386     
34387     /**
34388      * Returns this regions position (north/south/east/west/center).
34389      * @return {String} 
34390      */
34391     getPosition: function(){
34392         return this.position;    
34393     }
34394 });/*
34395  * Based on:
34396  * Ext JS Library 1.1.1
34397  * Copyright(c) 2006-2007, Ext JS, LLC.
34398  *
34399  * Originally Released Under LGPL - original licence link has changed is not relivant.
34400  *
34401  * Fork - LGPL
34402  * <script type="text/javascript">
34403  */
34404  
34405 /**
34406  * @class Roo.LayoutRegion
34407  * @extends Roo.BasicLayoutRegion
34408  * This class represents a region in a layout manager.
34409  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
34410  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
34411  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
34412  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34413  * @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})
34414  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34415  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
34416  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34417  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34418  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34419  * @cfg {String}    title           The title for the region (overrides panel titles)
34420  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34421  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34422  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34423  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34424  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34425  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34426  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34427  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34428  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34429  * @cfg {Boolean}   showPin         True to show a pin button
34430  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34431  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34432  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34433  * @cfg {Number}    width           For East/West panels
34434  * @cfg {Number}    height          For North/South panels
34435  * @cfg {Boolean}   split           To show the splitter
34436  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34437  */
34438 Roo.LayoutRegion = function(mgr, config, pos){
34439     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34440     var dh = Roo.DomHelper;
34441     /** This region's container element 
34442     * @type Roo.Element */
34443     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34444     /** This region's title element 
34445     * @type Roo.Element */
34446
34447     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34448         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34449         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34450     ]}, true);
34451     this.titleEl.enableDisplayMode();
34452     /** This region's title text element 
34453     * @type HTMLElement */
34454     this.titleTextEl = this.titleEl.dom.firstChild;
34455     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34456     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34457     this.closeBtn.enableDisplayMode();
34458     this.closeBtn.on("click", this.closeClicked, this);
34459     this.closeBtn.hide();
34460
34461     this.createBody(config);
34462     this.visible = true;
34463     this.collapsed = false;
34464
34465     if(config.hideWhenEmpty){
34466         this.hide();
34467         this.on("paneladded", this.validateVisibility, this);
34468         this.on("panelremoved", this.validateVisibility, this);
34469     }
34470     this.applyConfig(config);
34471 };
34472
34473 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34474
34475     createBody : function(){
34476         /** This region's body element 
34477         * @type Roo.Element */
34478         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34479     },
34480
34481     applyConfig : function(c){
34482         if(c.collapsible && this.position != "center" && !this.collapsedEl){
34483             var dh = Roo.DomHelper;
34484             if(c.titlebar !== false){
34485                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34486                 this.collapseBtn.on("click", this.collapse, this);
34487                 this.collapseBtn.enableDisplayMode();
34488
34489                 if(c.showPin === true || this.showPin){
34490                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34491                     this.stickBtn.enableDisplayMode();
34492                     this.stickBtn.on("click", this.expand, this);
34493                     this.stickBtn.hide();
34494                 }
34495             }
34496             /** This region's collapsed element
34497             * @type Roo.Element */
34498             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34499                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34500             ]}, true);
34501             if(c.floatable !== false){
34502                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34503                this.collapsedEl.on("click", this.collapseClick, this);
34504             }
34505
34506             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34507                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34508                    id: "message", unselectable: "on", style:{"float":"left"}});
34509                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34510              }
34511             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34512             this.expandBtn.on("click", this.expand, this);
34513         }
34514         if(this.collapseBtn){
34515             this.collapseBtn.setVisible(c.collapsible == true);
34516         }
34517         this.cmargins = c.cmargins || this.cmargins ||
34518                          (this.position == "west" || this.position == "east" ?
34519                              {top: 0, left: 2, right:2, bottom: 0} :
34520                              {top: 2, left: 0, right:0, bottom: 2});
34521         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34522         this.bottomTabs = c.tabPosition != "top";
34523         this.autoScroll = c.autoScroll || false;
34524         if(this.autoScroll){
34525             this.bodyEl.setStyle("overflow", "auto");
34526         }else{
34527             this.bodyEl.setStyle("overflow", "hidden");
34528         }
34529         //if(c.titlebar !== false){
34530             if((!c.titlebar && !c.title) || c.titlebar === false){
34531                 this.titleEl.hide();
34532             }else{
34533                 this.titleEl.show();
34534                 if(c.title){
34535                     this.titleTextEl.innerHTML = c.title;
34536                 }
34537             }
34538         //}
34539         this.duration = c.duration || .30;
34540         this.slideDuration = c.slideDuration || .45;
34541         this.config = c;
34542         if(c.collapsed){
34543             this.collapse(true);
34544         }
34545         if(c.hidden){
34546             this.hide();
34547         }
34548     },
34549     /**
34550      * Returns true if this region is currently visible.
34551      * @return {Boolean}
34552      */
34553     isVisible : function(){
34554         return this.visible;
34555     },
34556
34557     /**
34558      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34559      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34560      */
34561     setCollapsedTitle : function(title){
34562         title = title || "&#160;";
34563         if(this.collapsedTitleTextEl){
34564             this.collapsedTitleTextEl.innerHTML = title;
34565         }
34566     },
34567
34568     getBox : function(){
34569         var b;
34570         if(!this.collapsed){
34571             b = this.el.getBox(false, true);
34572         }else{
34573             b = this.collapsedEl.getBox(false, true);
34574         }
34575         return b;
34576     },
34577
34578     getMargins : function(){
34579         return this.collapsed ? this.cmargins : this.margins;
34580     },
34581
34582     highlight : function(){
34583         this.el.addClass("x-layout-panel-dragover");
34584     },
34585
34586     unhighlight : function(){
34587         this.el.removeClass("x-layout-panel-dragover");
34588     },
34589
34590     updateBox : function(box){
34591         this.box = box;
34592         if(!this.collapsed){
34593             this.el.dom.style.left = box.x + "px";
34594             this.el.dom.style.top = box.y + "px";
34595             this.updateBody(box.width, box.height);
34596         }else{
34597             this.collapsedEl.dom.style.left = box.x + "px";
34598             this.collapsedEl.dom.style.top = box.y + "px";
34599             this.collapsedEl.setSize(box.width, box.height);
34600         }
34601         if(this.tabs){
34602             this.tabs.autoSizeTabs();
34603         }
34604     },
34605
34606     updateBody : function(w, h){
34607         if(w !== null){
34608             this.el.setWidth(w);
34609             w -= this.el.getBorderWidth("rl");
34610             if(this.config.adjustments){
34611                 w += this.config.adjustments[0];
34612             }
34613         }
34614         if(h !== null){
34615             this.el.setHeight(h);
34616             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34617             h -= this.el.getBorderWidth("tb");
34618             if(this.config.adjustments){
34619                 h += this.config.adjustments[1];
34620             }
34621             this.bodyEl.setHeight(h);
34622             if(this.tabs){
34623                 h = this.tabs.syncHeight(h);
34624             }
34625         }
34626         if(this.panelSize){
34627             w = w !== null ? w : this.panelSize.width;
34628             h = h !== null ? h : this.panelSize.height;
34629         }
34630         if(this.activePanel){
34631             var el = this.activePanel.getEl();
34632             w = w !== null ? w : el.getWidth();
34633             h = h !== null ? h : el.getHeight();
34634             this.panelSize = {width: w, height: h};
34635             this.activePanel.setSize(w, h);
34636         }
34637         if(Roo.isIE && this.tabs){
34638             this.tabs.el.repaint();
34639         }
34640     },
34641
34642     /**
34643      * Returns the container element for this region.
34644      * @return {Roo.Element}
34645      */
34646     getEl : function(){
34647         return this.el;
34648     },
34649
34650     /**
34651      * Hides this region.
34652      */
34653     hide : function(){
34654         if(!this.collapsed){
34655             this.el.dom.style.left = "-2000px";
34656             this.el.hide();
34657         }else{
34658             this.collapsedEl.dom.style.left = "-2000px";
34659             this.collapsedEl.hide();
34660         }
34661         this.visible = false;
34662         this.fireEvent("visibilitychange", this, false);
34663     },
34664
34665     /**
34666      * Shows this region if it was previously hidden.
34667      */
34668     show : function(){
34669         if(!this.collapsed){
34670             this.el.show();
34671         }else{
34672             this.collapsedEl.show();
34673         }
34674         this.visible = true;
34675         this.fireEvent("visibilitychange", this, true);
34676     },
34677
34678     closeClicked : function(){
34679         if(this.activePanel){
34680             this.remove(this.activePanel);
34681         }
34682     },
34683
34684     collapseClick : function(e){
34685         if(this.isSlid){
34686            e.stopPropagation();
34687            this.slideIn();
34688         }else{
34689            e.stopPropagation();
34690            this.slideOut();
34691         }
34692     },
34693
34694     /**
34695      * Collapses this region.
34696      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34697      */
34698     collapse : function(skipAnim, skipCheck){
34699         if(this.collapsed) {
34700             return;
34701         }
34702         
34703         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34704             
34705             this.collapsed = true;
34706             if(this.split){
34707                 this.split.el.hide();
34708             }
34709             if(this.config.animate && skipAnim !== true){
34710                 this.fireEvent("invalidated", this);
34711                 this.animateCollapse();
34712             }else{
34713                 this.el.setLocation(-20000,-20000);
34714                 this.el.hide();
34715                 this.collapsedEl.show();
34716                 this.fireEvent("collapsed", this);
34717                 this.fireEvent("invalidated", this);
34718             }
34719         }
34720         
34721     },
34722
34723     animateCollapse : function(){
34724         // overridden
34725     },
34726
34727     /**
34728      * Expands this region if it was previously collapsed.
34729      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34730      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34731      */
34732     expand : function(e, skipAnim){
34733         if(e) {
34734             e.stopPropagation();
34735         }
34736         if(!this.collapsed || this.el.hasActiveFx()) {
34737             return;
34738         }
34739         if(this.isSlid){
34740             this.afterSlideIn();
34741             skipAnim = true;
34742         }
34743         this.collapsed = false;
34744         if(this.config.animate && skipAnim !== true){
34745             this.animateExpand();
34746         }else{
34747             this.el.show();
34748             if(this.split){
34749                 this.split.el.show();
34750             }
34751             this.collapsedEl.setLocation(-2000,-2000);
34752             this.collapsedEl.hide();
34753             this.fireEvent("invalidated", this);
34754             this.fireEvent("expanded", this);
34755         }
34756     },
34757
34758     animateExpand : function(){
34759         // overridden
34760     },
34761
34762     initTabs : function()
34763     {
34764         this.bodyEl.setStyle("overflow", "hidden");
34765         var ts = new Roo.TabPanel(
34766                 this.bodyEl.dom,
34767                 {
34768                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34769                     disableTooltips: this.config.disableTabTips,
34770                     toolbar : this.config.toolbar
34771                 }
34772         );
34773         if(this.config.hideTabs){
34774             ts.stripWrap.setDisplayed(false);
34775         }
34776         this.tabs = ts;
34777         ts.resizeTabs = this.config.resizeTabs === true;
34778         ts.minTabWidth = this.config.minTabWidth || 40;
34779         ts.maxTabWidth = this.config.maxTabWidth || 250;
34780         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34781         ts.monitorResize = false;
34782         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34783         ts.bodyEl.addClass('x-layout-tabs-body');
34784         this.panels.each(this.initPanelAsTab, this);
34785     },
34786
34787     initPanelAsTab : function(panel){
34788         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34789                     this.config.closeOnTab && panel.isClosable());
34790         if(panel.tabTip !== undefined){
34791             ti.setTooltip(panel.tabTip);
34792         }
34793         ti.on("activate", function(){
34794               this.setActivePanel(panel);
34795         }, this);
34796         if(this.config.closeOnTab){
34797             ti.on("beforeclose", function(t, e){
34798                 e.cancel = true;
34799                 this.remove(panel);
34800             }, this);
34801         }
34802         return ti;
34803     },
34804
34805     updatePanelTitle : function(panel, title){
34806         if(this.activePanel == panel){
34807             this.updateTitle(title);
34808         }
34809         if(this.tabs){
34810             var ti = this.tabs.getTab(panel.getEl().id);
34811             ti.setText(title);
34812             if(panel.tabTip !== undefined){
34813                 ti.setTooltip(panel.tabTip);
34814             }
34815         }
34816     },
34817
34818     updateTitle : function(title){
34819         if(this.titleTextEl && !this.config.title){
34820             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34821         }
34822     },
34823
34824     setActivePanel : function(panel){
34825         panel = this.getPanel(panel);
34826         if(this.activePanel && this.activePanel != panel){
34827             this.activePanel.setActiveState(false);
34828         }
34829         this.activePanel = panel;
34830         panel.setActiveState(true);
34831         if(this.panelSize){
34832             panel.setSize(this.panelSize.width, this.panelSize.height);
34833         }
34834         if(this.closeBtn){
34835             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34836         }
34837         this.updateTitle(panel.getTitle());
34838         if(this.tabs){
34839             this.fireEvent("invalidated", this);
34840         }
34841         this.fireEvent("panelactivated", this, panel);
34842     },
34843
34844     /**
34845      * Shows the specified panel.
34846      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34847      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34848      */
34849     showPanel : function(panel)
34850     {
34851         panel = this.getPanel(panel);
34852         if(panel){
34853             if(this.tabs){
34854                 var tab = this.tabs.getTab(panel.getEl().id);
34855                 if(tab.isHidden()){
34856                     this.tabs.unhideTab(tab.id);
34857                 }
34858                 tab.activate();
34859             }else{
34860                 this.setActivePanel(panel);
34861             }
34862         }
34863         return panel;
34864     },
34865
34866     /**
34867      * Get the active panel for this region.
34868      * @return {Roo.ContentPanel} The active panel or null
34869      */
34870     getActivePanel : function(){
34871         return this.activePanel;
34872     },
34873
34874     validateVisibility : function(){
34875         if(this.panels.getCount() < 1){
34876             this.updateTitle("&#160;");
34877             this.closeBtn.hide();
34878             this.hide();
34879         }else{
34880             if(!this.isVisible()){
34881                 this.show();
34882             }
34883         }
34884     },
34885
34886     /**
34887      * Adds the passed ContentPanel(s) to this region.
34888      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34889      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34890      */
34891     add : function(panel){
34892         if(arguments.length > 1){
34893             for(var i = 0, len = arguments.length; i < len; i++) {
34894                 this.add(arguments[i]);
34895             }
34896             return null;
34897         }
34898         if(this.hasPanel(panel)){
34899             this.showPanel(panel);
34900             return panel;
34901         }
34902         panel.setRegion(this);
34903         this.panels.add(panel);
34904         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34905             this.bodyEl.dom.appendChild(panel.getEl().dom);
34906             if(panel.background !== true){
34907                 this.setActivePanel(panel);
34908             }
34909             this.fireEvent("paneladded", this, panel);
34910             return panel;
34911         }
34912         if(!this.tabs){
34913             this.initTabs();
34914         }else{
34915             this.initPanelAsTab(panel);
34916         }
34917         if(panel.background !== true){
34918             this.tabs.activate(panel.getEl().id);
34919         }
34920         this.fireEvent("paneladded", this, panel);
34921         return panel;
34922     },
34923
34924     /**
34925      * Hides the tab for the specified panel.
34926      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34927      */
34928     hidePanel : function(panel){
34929         if(this.tabs && (panel = this.getPanel(panel))){
34930             this.tabs.hideTab(panel.getEl().id);
34931         }
34932     },
34933
34934     /**
34935      * Unhides the tab for a previously hidden panel.
34936      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34937      */
34938     unhidePanel : function(panel){
34939         if(this.tabs && (panel = this.getPanel(panel))){
34940             this.tabs.unhideTab(panel.getEl().id);
34941         }
34942     },
34943
34944     clearPanels : function(){
34945         while(this.panels.getCount() > 0){
34946              this.remove(this.panels.first());
34947         }
34948     },
34949
34950     /**
34951      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34952      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34953      * @param {Boolean} preservePanel Overrides the config preservePanel option
34954      * @return {Roo.ContentPanel} The panel that was removed
34955      */
34956     remove : function(panel, preservePanel){
34957         panel = this.getPanel(panel);
34958         if(!panel){
34959             return null;
34960         }
34961         var e = {};
34962         this.fireEvent("beforeremove", this, panel, e);
34963         if(e.cancel === true){
34964             return null;
34965         }
34966         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34967         var panelId = panel.getId();
34968         this.panels.removeKey(panelId);
34969         if(preservePanel){
34970             document.body.appendChild(panel.getEl().dom);
34971         }
34972         if(this.tabs){
34973             this.tabs.removeTab(panel.getEl().id);
34974         }else if (!preservePanel){
34975             this.bodyEl.dom.removeChild(panel.getEl().dom);
34976         }
34977         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34978             var p = this.panels.first();
34979             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34980             tempEl.appendChild(p.getEl().dom);
34981             this.bodyEl.update("");
34982             this.bodyEl.dom.appendChild(p.getEl().dom);
34983             tempEl = null;
34984             this.updateTitle(p.getTitle());
34985             this.tabs = null;
34986             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34987             this.setActivePanel(p);
34988         }
34989         panel.setRegion(null);
34990         if(this.activePanel == panel){
34991             this.activePanel = null;
34992         }
34993         if(this.config.autoDestroy !== false && preservePanel !== true){
34994             try{panel.destroy();}catch(e){}
34995         }
34996         this.fireEvent("panelremoved", this, panel);
34997         return panel;
34998     },
34999
35000     /**
35001      * Returns the TabPanel component used by this region
35002      * @return {Roo.TabPanel}
35003      */
35004     getTabs : function(){
35005         return this.tabs;
35006     },
35007
35008     createTool : function(parentEl, className){
35009         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35010             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
35011         btn.addClassOnOver("x-layout-tools-button-over");
35012         return btn;
35013     }
35014 });/*
35015  * Based on:
35016  * Ext JS Library 1.1.1
35017  * Copyright(c) 2006-2007, Ext JS, LLC.
35018  *
35019  * Originally Released Under LGPL - original licence link has changed is not relivant.
35020  *
35021  * Fork - LGPL
35022  * <script type="text/javascript">
35023  */
35024  
35025
35026
35027 /**
35028  * @class Roo.SplitLayoutRegion
35029  * @extends Roo.LayoutRegion
35030  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35031  */
35032 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35033     this.cursor = cursor;
35034     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35035 };
35036
35037 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35038     splitTip : "Drag to resize.",
35039     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35040     useSplitTips : false,
35041
35042     applyConfig : function(config){
35043         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35044         if(config.split){
35045             if(!this.split){
35046                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
35047                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
35048                 /** The SplitBar for this region 
35049                 * @type Roo.SplitBar */
35050                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35051                 this.split.on("moved", this.onSplitMove, this);
35052                 this.split.useShim = config.useShim === true;
35053                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35054                 if(this.useSplitTips){
35055                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35056                 }
35057                 if(config.collapsible){
35058                     this.split.el.on("dblclick", this.collapse,  this);
35059                 }
35060             }
35061             if(typeof config.minSize != "undefined"){
35062                 this.split.minSize = config.minSize;
35063             }
35064             if(typeof config.maxSize != "undefined"){
35065                 this.split.maxSize = config.maxSize;
35066             }
35067             if(config.hideWhenEmpty || config.hidden || config.collapsed){
35068                 this.hideSplitter();
35069             }
35070         }
35071     },
35072
35073     getHMaxSize : function(){
35074          var cmax = this.config.maxSize || 10000;
35075          var center = this.mgr.getRegion("center");
35076          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35077     },
35078
35079     getVMaxSize : function(){
35080          var cmax = this.config.maxSize || 10000;
35081          var center = this.mgr.getRegion("center");
35082          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35083     },
35084
35085     onSplitMove : function(split, newSize){
35086         this.fireEvent("resized", this, newSize);
35087     },
35088     
35089     /** 
35090      * Returns the {@link Roo.SplitBar} for this region.
35091      * @return {Roo.SplitBar}
35092      */
35093     getSplitBar : function(){
35094         return this.split;
35095     },
35096     
35097     hide : function(){
35098         this.hideSplitter();
35099         Roo.SplitLayoutRegion.superclass.hide.call(this);
35100     },
35101
35102     hideSplitter : function(){
35103         if(this.split){
35104             this.split.el.setLocation(-2000,-2000);
35105             this.split.el.hide();
35106         }
35107     },
35108
35109     show : function(){
35110         if(this.split){
35111             this.split.el.show();
35112         }
35113         Roo.SplitLayoutRegion.superclass.show.call(this);
35114     },
35115     
35116     beforeSlide: function(){
35117         if(Roo.isGecko){// firefox overflow auto bug workaround
35118             this.bodyEl.clip();
35119             if(this.tabs) {
35120                 this.tabs.bodyEl.clip();
35121             }
35122             if(this.activePanel){
35123                 this.activePanel.getEl().clip();
35124                 
35125                 if(this.activePanel.beforeSlide){
35126                     this.activePanel.beforeSlide();
35127                 }
35128             }
35129         }
35130     },
35131     
35132     afterSlide : function(){
35133         if(Roo.isGecko){// firefox overflow auto bug workaround
35134             this.bodyEl.unclip();
35135             if(this.tabs) {
35136                 this.tabs.bodyEl.unclip();
35137             }
35138             if(this.activePanel){
35139                 this.activePanel.getEl().unclip();
35140                 if(this.activePanel.afterSlide){
35141                     this.activePanel.afterSlide();
35142                 }
35143             }
35144         }
35145     },
35146
35147     initAutoHide : function(){
35148         if(this.autoHide !== false){
35149             if(!this.autoHideHd){
35150                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35151                 this.autoHideHd = {
35152                     "mouseout": function(e){
35153                         if(!e.within(this.el, true)){
35154                             st.delay(500);
35155                         }
35156                     },
35157                     "mouseover" : function(e){
35158                         st.cancel();
35159                     },
35160                     scope : this
35161                 };
35162             }
35163             this.el.on(this.autoHideHd);
35164         }
35165     },
35166
35167     clearAutoHide : function(){
35168         if(this.autoHide !== false){
35169             this.el.un("mouseout", this.autoHideHd.mouseout);
35170             this.el.un("mouseover", this.autoHideHd.mouseover);
35171         }
35172     },
35173
35174     clearMonitor : function(){
35175         Roo.get(document).un("click", this.slideInIf, this);
35176     },
35177
35178     // these names are backwards but not changed for compat
35179     slideOut : function(){
35180         if(this.isSlid || this.el.hasActiveFx()){
35181             return;
35182         }
35183         this.isSlid = true;
35184         if(this.collapseBtn){
35185             this.collapseBtn.hide();
35186         }
35187         this.closeBtnState = this.closeBtn.getStyle('display');
35188         this.closeBtn.hide();
35189         if(this.stickBtn){
35190             this.stickBtn.show();
35191         }
35192         this.el.show();
35193         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35194         this.beforeSlide();
35195         this.el.setStyle("z-index", 10001);
35196         this.el.slideIn(this.getSlideAnchor(), {
35197             callback: function(){
35198                 this.afterSlide();
35199                 this.initAutoHide();
35200                 Roo.get(document).on("click", this.slideInIf, this);
35201                 this.fireEvent("slideshow", this);
35202             },
35203             scope: this,
35204             block: true
35205         });
35206     },
35207
35208     afterSlideIn : function(){
35209         this.clearAutoHide();
35210         this.isSlid = false;
35211         this.clearMonitor();
35212         this.el.setStyle("z-index", "");
35213         if(this.collapseBtn){
35214             this.collapseBtn.show();
35215         }
35216         this.closeBtn.setStyle('display', this.closeBtnState);
35217         if(this.stickBtn){
35218             this.stickBtn.hide();
35219         }
35220         this.fireEvent("slidehide", this);
35221     },
35222
35223     slideIn : function(cb){
35224         if(!this.isSlid || this.el.hasActiveFx()){
35225             Roo.callback(cb);
35226             return;
35227         }
35228         this.isSlid = false;
35229         this.beforeSlide();
35230         this.el.slideOut(this.getSlideAnchor(), {
35231             callback: function(){
35232                 this.el.setLeftTop(-10000, -10000);
35233                 this.afterSlide();
35234                 this.afterSlideIn();
35235                 Roo.callback(cb);
35236             },
35237             scope: this,
35238             block: true
35239         });
35240     },
35241     
35242     slideInIf : function(e){
35243         if(!e.within(this.el)){
35244             this.slideIn();
35245         }
35246     },
35247
35248     animateCollapse : function(){
35249         this.beforeSlide();
35250         this.el.setStyle("z-index", 20000);
35251         var anchor = this.getSlideAnchor();
35252         this.el.slideOut(anchor, {
35253             callback : function(){
35254                 this.el.setStyle("z-index", "");
35255                 this.collapsedEl.slideIn(anchor, {duration:.3});
35256                 this.afterSlide();
35257                 this.el.setLocation(-10000,-10000);
35258                 this.el.hide();
35259                 this.fireEvent("collapsed", this);
35260             },
35261             scope: this,
35262             block: true
35263         });
35264     },
35265
35266     animateExpand : function(){
35267         this.beforeSlide();
35268         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35269         this.el.setStyle("z-index", 20000);
35270         this.collapsedEl.hide({
35271             duration:.1
35272         });
35273         this.el.slideIn(this.getSlideAnchor(), {
35274             callback : function(){
35275                 this.el.setStyle("z-index", "");
35276                 this.afterSlide();
35277                 if(this.split){
35278                     this.split.el.show();
35279                 }
35280                 this.fireEvent("invalidated", this);
35281                 this.fireEvent("expanded", this);
35282             },
35283             scope: this,
35284             block: true
35285         });
35286     },
35287
35288     anchors : {
35289         "west" : "left",
35290         "east" : "right",
35291         "north" : "top",
35292         "south" : "bottom"
35293     },
35294
35295     sanchors : {
35296         "west" : "l",
35297         "east" : "r",
35298         "north" : "t",
35299         "south" : "b"
35300     },
35301
35302     canchors : {
35303         "west" : "tl-tr",
35304         "east" : "tr-tl",
35305         "north" : "tl-bl",
35306         "south" : "bl-tl"
35307     },
35308
35309     getAnchor : function(){
35310         return this.anchors[this.position];
35311     },
35312
35313     getCollapseAnchor : function(){
35314         return this.canchors[this.position];
35315     },
35316
35317     getSlideAnchor : function(){
35318         return this.sanchors[this.position];
35319     },
35320
35321     getAlignAdj : function(){
35322         var cm = this.cmargins;
35323         switch(this.position){
35324             case "west":
35325                 return [0, 0];
35326             break;
35327             case "east":
35328                 return [0, 0];
35329             break;
35330             case "north":
35331                 return [0, 0];
35332             break;
35333             case "south":
35334                 return [0, 0];
35335             break;
35336         }
35337     },
35338
35339     getExpandAdj : function(){
35340         var c = this.collapsedEl, cm = this.cmargins;
35341         switch(this.position){
35342             case "west":
35343                 return [-(cm.right+c.getWidth()+cm.left), 0];
35344             break;
35345             case "east":
35346                 return [cm.right+c.getWidth()+cm.left, 0];
35347             break;
35348             case "north":
35349                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35350             break;
35351             case "south":
35352                 return [0, cm.top+cm.bottom+c.getHeight()];
35353             break;
35354         }
35355     }
35356 });/*
35357  * Based on:
35358  * Ext JS Library 1.1.1
35359  * Copyright(c) 2006-2007, Ext JS, LLC.
35360  *
35361  * Originally Released Under LGPL - original licence link has changed is not relivant.
35362  *
35363  * Fork - LGPL
35364  * <script type="text/javascript">
35365  */
35366 /*
35367  * These classes are private internal classes
35368  */
35369 Roo.CenterLayoutRegion = function(mgr, config){
35370     Roo.LayoutRegion.call(this, mgr, config, "center");
35371     this.visible = true;
35372     this.minWidth = config.minWidth || 20;
35373     this.minHeight = config.minHeight || 20;
35374 };
35375
35376 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35377     hide : function(){
35378         // center panel can't be hidden
35379     },
35380     
35381     show : function(){
35382         // center panel can't be hidden
35383     },
35384     
35385     getMinWidth: function(){
35386         return this.minWidth;
35387     },
35388     
35389     getMinHeight: function(){
35390         return this.minHeight;
35391     }
35392 });
35393
35394
35395 Roo.NorthLayoutRegion = function(mgr, config){
35396     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35397     if(this.split){
35398         this.split.placement = Roo.SplitBar.TOP;
35399         this.split.orientation = Roo.SplitBar.VERTICAL;
35400         this.split.el.addClass("x-layout-split-v");
35401     }
35402     var size = config.initialSize || config.height;
35403     if(typeof size != "undefined"){
35404         this.el.setHeight(size);
35405     }
35406 };
35407 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35408     orientation: Roo.SplitBar.VERTICAL,
35409     getBox : function(){
35410         if(this.collapsed){
35411             return this.collapsedEl.getBox();
35412         }
35413         var box = this.el.getBox();
35414         if(this.split){
35415             box.height += this.split.el.getHeight();
35416         }
35417         return box;
35418     },
35419     
35420     updateBox : function(box){
35421         if(this.split && !this.collapsed){
35422             box.height -= this.split.el.getHeight();
35423             this.split.el.setLeft(box.x);
35424             this.split.el.setTop(box.y+box.height);
35425             this.split.el.setWidth(box.width);
35426         }
35427         if(this.collapsed){
35428             this.updateBody(box.width, null);
35429         }
35430         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35431     }
35432 });
35433
35434 Roo.SouthLayoutRegion = function(mgr, config){
35435     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35436     if(this.split){
35437         this.split.placement = Roo.SplitBar.BOTTOM;
35438         this.split.orientation = Roo.SplitBar.VERTICAL;
35439         this.split.el.addClass("x-layout-split-v");
35440     }
35441     var size = config.initialSize || config.height;
35442     if(typeof size != "undefined"){
35443         this.el.setHeight(size);
35444     }
35445 };
35446 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35447     orientation: Roo.SplitBar.VERTICAL,
35448     getBox : function(){
35449         if(this.collapsed){
35450             return this.collapsedEl.getBox();
35451         }
35452         var box = this.el.getBox();
35453         if(this.split){
35454             var sh = this.split.el.getHeight();
35455             box.height += sh;
35456             box.y -= sh;
35457         }
35458         return box;
35459     },
35460     
35461     updateBox : function(box){
35462         if(this.split && !this.collapsed){
35463             var sh = this.split.el.getHeight();
35464             box.height -= sh;
35465             box.y += sh;
35466             this.split.el.setLeft(box.x);
35467             this.split.el.setTop(box.y-sh);
35468             this.split.el.setWidth(box.width);
35469         }
35470         if(this.collapsed){
35471             this.updateBody(box.width, null);
35472         }
35473         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35474     }
35475 });
35476
35477 Roo.EastLayoutRegion = function(mgr, config){
35478     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35479     if(this.split){
35480         this.split.placement = Roo.SplitBar.RIGHT;
35481         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35482         this.split.el.addClass("x-layout-split-h");
35483     }
35484     var size = config.initialSize || config.width;
35485     if(typeof size != "undefined"){
35486         this.el.setWidth(size);
35487     }
35488 };
35489 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35490     orientation: Roo.SplitBar.HORIZONTAL,
35491     getBox : function(){
35492         if(this.collapsed){
35493             return this.collapsedEl.getBox();
35494         }
35495         var box = this.el.getBox();
35496         if(this.split){
35497             var sw = this.split.el.getWidth();
35498             box.width += sw;
35499             box.x -= sw;
35500         }
35501         return box;
35502     },
35503
35504     updateBox : function(box){
35505         if(this.split && !this.collapsed){
35506             var sw = this.split.el.getWidth();
35507             box.width -= sw;
35508             this.split.el.setLeft(box.x);
35509             this.split.el.setTop(box.y);
35510             this.split.el.setHeight(box.height);
35511             box.x += sw;
35512         }
35513         if(this.collapsed){
35514             this.updateBody(null, box.height);
35515         }
35516         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35517     }
35518 });
35519
35520 Roo.WestLayoutRegion = function(mgr, config){
35521     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35522     if(this.split){
35523         this.split.placement = Roo.SplitBar.LEFT;
35524         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35525         this.split.el.addClass("x-layout-split-h");
35526     }
35527     var size = config.initialSize || config.width;
35528     if(typeof size != "undefined"){
35529         this.el.setWidth(size);
35530     }
35531 };
35532 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35533     orientation: Roo.SplitBar.HORIZONTAL,
35534     getBox : function(){
35535         if(this.collapsed){
35536             return this.collapsedEl.getBox();
35537         }
35538         var box = this.el.getBox();
35539         if(this.split){
35540             box.width += this.split.el.getWidth();
35541         }
35542         return box;
35543     },
35544     
35545     updateBox : function(box){
35546         if(this.split && !this.collapsed){
35547             var sw = this.split.el.getWidth();
35548             box.width -= sw;
35549             this.split.el.setLeft(box.x+box.width);
35550             this.split.el.setTop(box.y);
35551             this.split.el.setHeight(box.height);
35552         }
35553         if(this.collapsed){
35554             this.updateBody(null, box.height);
35555         }
35556         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35557     }
35558 });
35559 /*
35560  * Based on:
35561  * Ext JS Library 1.1.1
35562  * Copyright(c) 2006-2007, Ext JS, LLC.
35563  *
35564  * Originally Released Under LGPL - original licence link has changed is not relivant.
35565  *
35566  * Fork - LGPL
35567  * <script type="text/javascript">
35568  */
35569  
35570  
35571 /*
35572  * Private internal class for reading and applying state
35573  */
35574 Roo.LayoutStateManager = function(layout){
35575      // default empty state
35576      this.state = {
35577         north: {},
35578         south: {},
35579         east: {},
35580         west: {}       
35581     };
35582 };
35583
35584 Roo.LayoutStateManager.prototype = {
35585     init : function(layout, provider){
35586         this.provider = provider;
35587         var state = provider.get(layout.id+"-layout-state");
35588         if(state){
35589             var wasUpdating = layout.isUpdating();
35590             if(!wasUpdating){
35591                 layout.beginUpdate();
35592             }
35593             for(var key in state){
35594                 if(typeof state[key] != "function"){
35595                     var rstate = state[key];
35596                     var r = layout.getRegion(key);
35597                     if(r && rstate){
35598                         if(rstate.size){
35599                             r.resizeTo(rstate.size);
35600                         }
35601                         if(rstate.collapsed == true){
35602                             r.collapse(true);
35603                         }else{
35604                             r.expand(null, true);
35605                         }
35606                     }
35607                 }
35608             }
35609             if(!wasUpdating){
35610                 layout.endUpdate();
35611             }
35612             this.state = state; 
35613         }
35614         this.layout = layout;
35615         layout.on("regionresized", this.onRegionResized, this);
35616         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35617         layout.on("regionexpanded", this.onRegionExpanded, this);
35618     },
35619     
35620     storeState : function(){
35621         this.provider.set(this.layout.id+"-layout-state", this.state);
35622     },
35623     
35624     onRegionResized : function(region, newSize){
35625         this.state[region.getPosition()].size = newSize;
35626         this.storeState();
35627     },
35628     
35629     onRegionCollapsed : function(region){
35630         this.state[region.getPosition()].collapsed = true;
35631         this.storeState();
35632     },
35633     
35634     onRegionExpanded : function(region){
35635         this.state[region.getPosition()].collapsed = false;
35636         this.storeState();
35637     }
35638 };/*
35639  * Based on:
35640  * Ext JS Library 1.1.1
35641  * Copyright(c) 2006-2007, Ext JS, LLC.
35642  *
35643  * Originally Released Under LGPL - original licence link has changed is not relivant.
35644  *
35645  * Fork - LGPL
35646  * <script type="text/javascript">
35647  */
35648 /**
35649  * @class Roo.ContentPanel
35650  * @extends Roo.util.Observable
35651  * @children Roo.form.Form Roo.JsonView Roo.View
35652  * @parent Roo.BorderLayout Roo.LayoutDialog builder
35653  * A basic ContentPanel element.
35654  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35655  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35656  * @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
35657  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35658  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35659  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35660  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
35661  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35662  * @cfg {String} title          The title for this panel
35663  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35664  * @cfg {String} url            Calls {@link #setUrl} with this value
35665  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35666  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35667  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35668  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35669  * @cfg {String}    style  Extra style to add to the content panel
35670  * @cfg {Roo.menu.Menu} menu  popup menu
35671
35672  * @constructor
35673  * Create a new ContentPanel.
35674  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35675  * @param {String/Object} config A string to set only the title or a config object
35676  * @param {String} content (optional) Set the HTML content for this panel
35677  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35678  */
35679 Roo.ContentPanel = function(el, config, content){
35680     
35681      
35682     /*
35683     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35684         config = el;
35685         el = Roo.id();
35686     }
35687     if (config && config.parentLayout) { 
35688         el = config.parentLayout.el.createChild(); 
35689     }
35690     */
35691     if(el.autoCreate){ // xtype is available if this is called from factory
35692         config = el;
35693         el = Roo.id();
35694     }
35695     this.el = Roo.get(el);
35696     if(!this.el && config && config.autoCreate){
35697         if(typeof config.autoCreate == "object"){
35698             if(!config.autoCreate.id){
35699                 config.autoCreate.id = config.id||el;
35700             }
35701             this.el = Roo.DomHelper.append(document.body,
35702                         config.autoCreate, true);
35703         }else{
35704             this.el = Roo.DomHelper.append(document.body,
35705                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35706         }
35707     }
35708     
35709     
35710     this.closable = false;
35711     this.loaded = false;
35712     this.active = false;
35713     if(typeof config == "string"){
35714         this.title = config;
35715     }else{
35716         Roo.apply(this, config);
35717     }
35718     
35719     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35720         this.wrapEl = this.el.wrap();
35721         this.toolbar.container = this.el.insertSibling(false, 'before');
35722         this.toolbar = new Roo.Toolbar(this.toolbar);
35723     }
35724     
35725     // xtype created footer. - not sure if will work as we normally have to render first..
35726     if (this.footer && !this.footer.el && this.footer.xtype) {
35727         if (!this.wrapEl) {
35728             this.wrapEl = this.el.wrap();
35729         }
35730     
35731         this.footer.container = this.wrapEl.createChild();
35732          
35733         this.footer = Roo.factory(this.footer, Roo);
35734         
35735     }
35736     
35737     if(this.resizeEl){
35738         this.resizeEl = Roo.get(this.resizeEl, true);
35739     }else{
35740         this.resizeEl = this.el;
35741     }
35742     // handle view.xtype
35743     
35744  
35745     
35746     
35747     this.addEvents({
35748         /**
35749          * @event activate
35750          * Fires when this panel is activated. 
35751          * @param {Roo.ContentPanel} this
35752          */
35753         "activate" : true,
35754         /**
35755          * @event deactivate
35756          * Fires when this panel is activated. 
35757          * @param {Roo.ContentPanel} this
35758          */
35759         "deactivate" : true,
35760
35761         /**
35762          * @event resize
35763          * Fires when this panel is resized if fitToFrame is true.
35764          * @param {Roo.ContentPanel} this
35765          * @param {Number} width The width after any component adjustments
35766          * @param {Number} height The height after any component adjustments
35767          */
35768         "resize" : true,
35769         
35770          /**
35771          * @event render
35772          * Fires when this tab is created
35773          * @param {Roo.ContentPanel} this
35774          */
35775         "render" : true
35776          
35777         
35778     });
35779     
35780
35781     
35782     
35783     if(this.autoScroll){
35784         this.resizeEl.setStyle("overflow", "auto");
35785     } else {
35786         // fix randome scrolling
35787         this.el.on('scroll', function() {
35788             Roo.log('fix random scolling');
35789             this.scrollTo('top',0); 
35790         });
35791     }
35792     content = content || this.content;
35793     if(content){
35794         this.setContent(content);
35795     }
35796     if(config && config.url){
35797         this.setUrl(this.url, this.params, this.loadOnce);
35798     }
35799     
35800     
35801     
35802     Roo.ContentPanel.superclass.constructor.call(this);
35803     
35804     if (this.view && typeof(this.view.xtype) != 'undefined') {
35805         this.view.el = this.el.appendChild(document.createElement("div"));
35806         this.view = Roo.factory(this.view); 
35807         this.view.render  &&  this.view.render(false, '');  
35808     }
35809     
35810     
35811     this.fireEvent('render', this);
35812 };
35813
35814 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35815     tabTip:'',
35816     setRegion : function(region){
35817         this.region = region;
35818         if(region){
35819            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35820         }else{
35821            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35822         } 
35823     },
35824     
35825     /**
35826      * Returns the toolbar for this Panel if one was configured. 
35827      * @return {Roo.Toolbar} 
35828      */
35829     getToolbar : function(){
35830         return this.toolbar;
35831     },
35832     
35833     setActiveState : function(active){
35834         this.active = active;
35835         if(!active){
35836             this.fireEvent("deactivate", this);
35837         }else{
35838             this.fireEvent("activate", this);
35839         }
35840     },
35841     /**
35842      * Updates this panel's element
35843      * @param {String} content The new content
35844      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35845     */
35846     setContent : function(content, loadScripts){
35847         this.el.update(content, loadScripts);
35848     },
35849
35850     ignoreResize : function(w, h){
35851         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35852             return true;
35853         }else{
35854             this.lastSize = {width: w, height: h};
35855             return false;
35856         }
35857     },
35858     /**
35859      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35860      * @return {Roo.UpdateManager} The UpdateManager
35861      */
35862     getUpdateManager : function(){
35863         return this.el.getUpdateManager();
35864     },
35865      /**
35866      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35867      * @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:
35868 <pre><code>
35869 panel.load({
35870     url: "your-url.php",
35871     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35872     callback: yourFunction,
35873     scope: yourObject, //(optional scope)
35874     discardUrl: false,
35875     nocache: false,
35876     text: "Loading...",
35877     timeout: 30,
35878     scripts: false
35879 });
35880 </code></pre>
35881      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35882      * 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.
35883      * @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}
35884      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35885      * @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.
35886      * @return {Roo.ContentPanel} this
35887      */
35888     load : function(){
35889         var um = this.el.getUpdateManager();
35890         um.update.apply(um, arguments);
35891         return this;
35892     },
35893
35894
35895     /**
35896      * 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.
35897      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35898      * @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)
35899      * @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)
35900      * @return {Roo.UpdateManager} The UpdateManager
35901      */
35902     setUrl : function(url, params, loadOnce){
35903         if(this.refreshDelegate){
35904             this.removeListener("activate", this.refreshDelegate);
35905         }
35906         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35907         this.on("activate", this.refreshDelegate);
35908         return this.el.getUpdateManager();
35909     },
35910     
35911     _handleRefresh : function(url, params, loadOnce){
35912         if(!loadOnce || !this.loaded){
35913             var updater = this.el.getUpdateManager();
35914             updater.update(url, params, this._setLoaded.createDelegate(this));
35915         }
35916     },
35917     
35918     _setLoaded : function(){
35919         this.loaded = true;
35920     }, 
35921     
35922     /**
35923      * Returns this panel's id
35924      * @return {String} 
35925      */
35926     getId : function(){
35927         return this.el.id;
35928     },
35929     
35930     /** 
35931      * Returns this panel's element - used by regiosn to add.
35932      * @return {Roo.Element} 
35933      */
35934     getEl : function(){
35935         return this.wrapEl || this.el;
35936     },
35937     
35938     adjustForComponents : function(width, height)
35939     {
35940         //Roo.log('adjustForComponents ');
35941         if(this.resizeEl != this.el){
35942             width -= this.el.getFrameWidth('lr');
35943             height -= this.el.getFrameWidth('tb');
35944         }
35945         if(this.toolbar){
35946             var te = this.toolbar.getEl();
35947             height -= te.getHeight();
35948             te.setWidth(width);
35949         }
35950         if(this.footer){
35951             var te = this.footer.getEl();
35952             //Roo.log("footer:" + te.getHeight());
35953             
35954             height -= te.getHeight();
35955             te.setWidth(width);
35956         }
35957         
35958         
35959         if(this.adjustments){
35960             width += this.adjustments[0];
35961             height += this.adjustments[1];
35962         }
35963         return {"width": width, "height": height};
35964     },
35965     
35966     setSize : function(width, height){
35967         if(this.fitToFrame && !this.ignoreResize(width, height)){
35968             if(this.fitContainer && this.resizeEl != this.el){
35969                 this.el.setSize(width, height);
35970             }
35971             var size = this.adjustForComponents(width, height);
35972             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35973             this.fireEvent('resize', this, size.width, size.height);
35974         }
35975     },
35976     
35977     /**
35978      * Returns this panel's title
35979      * @return {String} 
35980      */
35981     getTitle : function(){
35982         return this.title;
35983     },
35984     
35985     /**
35986      * Set this panel's title
35987      * @param {String} title
35988      */
35989     setTitle : function(title){
35990         this.title = title;
35991         if(this.region){
35992             this.region.updatePanelTitle(this, title);
35993         }
35994     },
35995     
35996     /**
35997      * Returns true is this panel was configured to be closable
35998      * @return {Boolean} 
35999      */
36000     isClosable : function(){
36001         return this.closable;
36002     },
36003     
36004     beforeSlide : function(){
36005         this.el.clip();
36006         this.resizeEl.clip();
36007     },
36008     
36009     afterSlide : function(){
36010         this.el.unclip();
36011         this.resizeEl.unclip();
36012     },
36013     
36014     /**
36015      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36016      *   Will fail silently if the {@link #setUrl} method has not been called.
36017      *   This does not activate the panel, just updates its content.
36018      */
36019     refresh : function(){
36020         if(this.refreshDelegate){
36021            this.loaded = false;
36022            this.refreshDelegate();
36023         }
36024     },
36025     
36026     /**
36027      * Destroys this panel
36028      */
36029     destroy : function(){
36030         this.el.removeAllListeners();
36031         var tempEl = document.createElement("span");
36032         tempEl.appendChild(this.el.dom);
36033         tempEl.innerHTML = "";
36034         this.el.remove();
36035         this.el = null;
36036     },
36037     
36038     /**
36039      * form - if the content panel contains a form - this is a reference to it.
36040      * @type {Roo.form.Form}
36041      */
36042     form : false,
36043     /**
36044      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36045      *    This contains a reference to it.
36046      * @type {Roo.View}
36047      */
36048     view : false,
36049     
36050       /**
36051      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36052      * <pre><code>
36053
36054 layout.addxtype({
36055        xtype : 'Form',
36056        items: [ .... ]
36057    }
36058 );
36059
36060 </code></pre>
36061      * @param {Object} cfg Xtype definition of item to add.
36062      */
36063     
36064     addxtype : function(cfg) {
36065         // add form..
36066         if (cfg.xtype.match(/^Form$/)) {
36067             
36068             var el;
36069             //if (this.footer) {
36070             //    el = this.footer.container.insertSibling(false, 'before');
36071             //} else {
36072                 el = this.el.createChild();
36073             //}
36074
36075             this.form = new  Roo.form.Form(cfg);
36076             
36077             
36078             if ( this.form.allItems.length) {
36079                 this.form.render(el.dom);
36080             }
36081             return this.form;
36082         }
36083         // should only have one of theses..
36084         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36085             // views.. should not be just added - used named prop 'view''
36086             
36087             cfg.el = this.el.appendChild(document.createElement("div"));
36088             // factory?
36089             
36090             var ret = new Roo.factory(cfg);
36091              
36092              ret.render && ret.render(false, ''); // render blank..
36093             this.view = ret;
36094             return ret;
36095         }
36096         return false;
36097     }
36098 });
36099
36100 /**
36101  * @class Roo.GridPanel
36102  * @extends Roo.ContentPanel
36103  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36104  * @constructor
36105  * Create a new GridPanel.
36106  * @cfg {Roo.grid.Grid} grid The grid for this panel
36107  */
36108 Roo.GridPanel = function(grid, config){
36109     
36110     // universal ctor...
36111     if (typeof(grid.grid) != 'undefined') {
36112         config = grid;
36113         grid = config.grid;
36114     }
36115     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36116         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36117         
36118     this.wrapper.dom.appendChild(grid.getGridEl().dom);
36119     
36120     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36121     
36122     if(this.toolbar){
36123         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36124     }
36125     // xtype created footer. - not sure if will work as we normally have to render first..
36126     if (this.footer && !this.footer.el && this.footer.xtype) {
36127         
36128         this.footer.container = this.grid.getView().getFooterPanel(true);
36129         this.footer.dataSource = this.grid.dataSource;
36130         this.footer = Roo.factory(this.footer, Roo);
36131         
36132     }
36133     
36134     grid.monitorWindowResize = false; // turn off autosizing
36135     grid.autoHeight = false;
36136     grid.autoWidth = false;
36137     this.grid = grid;
36138     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36139 };
36140
36141 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36142     getId : function(){
36143         return this.grid.id;
36144     },
36145     
36146     /**
36147      * Returns the grid for this panel
36148      * @return {Roo.grid.Grid} 
36149      */
36150     getGrid : function(){
36151         return this.grid;    
36152     },
36153     
36154     setSize : function(width, height){
36155         if(!this.ignoreResize(width, height)){
36156             var grid = this.grid;
36157             var size = this.adjustForComponents(width, height);
36158             grid.getGridEl().setSize(size.width, size.height);
36159             grid.autoSize();
36160         }
36161     },
36162     
36163     beforeSlide : function(){
36164         this.grid.getView().scroller.clip();
36165     },
36166     
36167     afterSlide : function(){
36168         this.grid.getView().scroller.unclip();
36169     },
36170     
36171     destroy : function(){
36172         this.grid.destroy();
36173         delete this.grid;
36174         Roo.GridPanel.superclass.destroy.call(this); 
36175     }
36176 });
36177
36178
36179 /**
36180  * @class Roo.NestedLayoutPanel
36181  * @extends Roo.ContentPanel
36182  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36183  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
36184  *
36185  * 
36186  * @constructor
36187  * Create a new NestedLayoutPanel.
36188  * 
36189  * 
36190  * @param {Roo.BorderLayout} layout [required] The layout for this panel
36191  * @param {String/Object} config A string to set only the title or a config object
36192  */
36193 Roo.NestedLayoutPanel = function(layout, config)
36194 {
36195     // construct with only one argument..
36196     /* FIXME - implement nicer consturctors
36197     if (layout.layout) {
36198         config = layout;
36199         layout = config.layout;
36200         delete config.layout;
36201     }
36202     if (layout.xtype && !layout.getEl) {
36203         // then layout needs constructing..
36204         layout = Roo.factory(layout, Roo);
36205     }
36206     */
36207     
36208     
36209     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36210     
36211     layout.monitorWindowResize = false; // turn off autosizing
36212     this.layout = layout;
36213     this.layout.getEl().addClass("x-layout-nested-layout");
36214     
36215     
36216     
36217     
36218 };
36219
36220 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36221
36222     setSize : function(width, height){
36223         if(!this.ignoreResize(width, height)){
36224             var size = this.adjustForComponents(width, height);
36225             var el = this.layout.getEl();
36226             el.setSize(size.width, size.height);
36227             var touch = el.dom.offsetWidth;
36228             this.layout.layout();
36229             // ie requires a double layout on the first pass
36230             if(Roo.isIE && !this.initialized){
36231                 this.initialized = true;
36232                 this.layout.layout();
36233             }
36234         }
36235     },
36236     
36237     // activate all subpanels if not currently active..
36238     
36239     setActiveState : function(active){
36240         this.active = active;
36241         if(!active){
36242             this.fireEvent("deactivate", this);
36243             return;
36244         }
36245         
36246         this.fireEvent("activate", this);
36247         // not sure if this should happen before or after..
36248         if (!this.layout) {
36249             return; // should not happen..
36250         }
36251         var reg = false;
36252         for (var r in this.layout.regions) {
36253             reg = this.layout.getRegion(r);
36254             if (reg.getActivePanel()) {
36255                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36256                 reg.setActivePanel(reg.getActivePanel());
36257                 continue;
36258             }
36259             if (!reg.panels.length) {
36260                 continue;
36261             }
36262             reg.showPanel(reg.getPanel(0));
36263         }
36264         
36265         
36266         
36267         
36268     },
36269     
36270     /**
36271      * Returns the nested BorderLayout for this panel
36272      * @return {Roo.BorderLayout} 
36273      */
36274     getLayout : function(){
36275         return this.layout;
36276     },
36277     
36278      /**
36279      * Adds a xtype elements to the layout of the nested panel
36280      * <pre><code>
36281
36282 panel.addxtype({
36283        xtype : 'ContentPanel',
36284        region: 'west',
36285        items: [ .... ]
36286    }
36287 );
36288
36289 panel.addxtype({
36290         xtype : 'NestedLayoutPanel',
36291         region: 'west',
36292         layout: {
36293            center: { },
36294            west: { }   
36295         },
36296         items : [ ... list of content panels or nested layout panels.. ]
36297    }
36298 );
36299 </code></pre>
36300      * @param {Object} cfg Xtype definition of item to add.
36301      */
36302     addxtype : function(cfg) {
36303         return this.layout.addxtype(cfg);
36304     
36305     }
36306 });
36307
36308 Roo.ScrollPanel = function(el, config, content){
36309     config = config || {};
36310     config.fitToFrame = true;
36311     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36312     
36313     this.el.dom.style.overflow = "hidden";
36314     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36315     this.el.removeClass("x-layout-inactive-content");
36316     this.el.on("mousewheel", this.onWheel, this);
36317
36318     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
36319     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
36320     up.unselectable(); down.unselectable();
36321     up.on("click", this.scrollUp, this);
36322     down.on("click", this.scrollDown, this);
36323     up.addClassOnOver("x-scroller-btn-over");
36324     down.addClassOnOver("x-scroller-btn-over");
36325     up.addClassOnClick("x-scroller-btn-click");
36326     down.addClassOnClick("x-scroller-btn-click");
36327     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36328
36329     this.resizeEl = this.el;
36330     this.el = wrap; this.up = up; this.down = down;
36331 };
36332
36333 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36334     increment : 100,
36335     wheelIncrement : 5,
36336     scrollUp : function(){
36337         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36338     },
36339
36340     scrollDown : function(){
36341         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36342     },
36343
36344     afterScroll : function(){
36345         var el = this.resizeEl;
36346         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36347         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36348         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36349     },
36350
36351     setSize : function(){
36352         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36353         this.afterScroll();
36354     },
36355
36356     onWheel : function(e){
36357         var d = e.getWheelDelta();
36358         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36359         this.afterScroll();
36360         e.stopEvent();
36361     },
36362
36363     setContent : function(content, loadScripts){
36364         this.resizeEl.update(content, loadScripts);
36365     }
36366
36367 });
36368
36369
36370
36371 /**
36372  * @class Roo.TreePanel
36373  * @extends Roo.ContentPanel
36374  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36375  * Treepanel component
36376  * 
36377  * @constructor
36378  * Create a new TreePanel. - defaults to fit/scoll contents.
36379  * @param {String/Object} config A string to set only the panel's title, or a config object
36380  */
36381 Roo.TreePanel = function(config){
36382     var el = config.el;
36383     var tree = config.tree;
36384     delete config.tree; 
36385     delete config.el; // hopefull!
36386     
36387     // wrapper for IE7 strict & safari scroll issue
36388     
36389     var treeEl = el.createChild();
36390     config.resizeEl = treeEl;
36391     
36392     
36393     
36394     Roo.TreePanel.superclass.constructor.call(this, el, config);
36395  
36396  
36397     this.tree = new Roo.tree.TreePanel(treeEl , tree);
36398     //console.log(tree);
36399     this.on('activate', function()
36400     {
36401         if (this.tree.rendered) {
36402             return;
36403         }
36404         //console.log('render tree');
36405         this.tree.render();
36406     });
36407     // this should not be needed.. - it's actually the 'el' that resizes?
36408     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36409     
36410     //this.on('resize',  function (cp, w, h) {
36411     //        this.tree.innerCt.setWidth(w);
36412     //        this.tree.innerCt.setHeight(h);
36413     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
36414     //});
36415
36416         
36417     
36418 };
36419
36420 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
36421     fitToFrame : true,
36422     autoScroll : true,
36423     /*
36424      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36425      */
36426     tree : false
36427
36428 });
36429
36430
36431
36432
36433
36434
36435
36436
36437
36438
36439
36440 /*
36441  * Based on:
36442  * Ext JS Library 1.1.1
36443  * Copyright(c) 2006-2007, Ext JS, LLC.
36444  *
36445  * Originally Released Under LGPL - original licence link has changed is not relivant.
36446  *
36447  * Fork - LGPL
36448  * <script type="text/javascript">
36449  */
36450  
36451
36452 /**
36453  * @class Roo.ReaderLayout
36454  * @extends Roo.BorderLayout
36455  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
36456  * center region containing two nested regions (a top one for a list view and one for item preview below),
36457  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36458  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36459  * expedites the setup of the overall layout and regions for this common application style.
36460  * Example:
36461  <pre><code>
36462 var reader = new Roo.ReaderLayout();
36463 var CP = Roo.ContentPanel;  // shortcut for adding
36464
36465 reader.beginUpdate();
36466 reader.add("north", new CP("north", "North"));
36467 reader.add("west", new CP("west", {title: "West"}));
36468 reader.add("east", new CP("east", {title: "East"}));
36469
36470 reader.regions.listView.add(new CP("listView", "List"));
36471 reader.regions.preview.add(new CP("preview", "Preview"));
36472 reader.endUpdate();
36473 </code></pre>
36474 * @constructor
36475 * Create a new ReaderLayout
36476 * @param {Object} config Configuration options
36477 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36478 * document.body if omitted)
36479 */
36480 Roo.ReaderLayout = function(config, renderTo){
36481     var c = config || {size:{}};
36482     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36483         north: c.north !== false ? Roo.apply({
36484             split:false,
36485             initialSize: 32,
36486             titlebar: false
36487         }, c.north) : false,
36488         west: c.west !== false ? Roo.apply({
36489             split:true,
36490             initialSize: 200,
36491             minSize: 175,
36492             maxSize: 400,
36493             titlebar: true,
36494             collapsible: true,
36495             animate: true,
36496             margins:{left:5,right:0,bottom:5,top:5},
36497             cmargins:{left:5,right:5,bottom:5,top:5}
36498         }, c.west) : false,
36499         east: c.east !== false ? Roo.apply({
36500             split:true,
36501             initialSize: 200,
36502             minSize: 175,
36503             maxSize: 400,
36504             titlebar: true,
36505             collapsible: true,
36506             animate: true,
36507             margins:{left:0,right:5,bottom:5,top:5},
36508             cmargins:{left:5,right:5,bottom:5,top:5}
36509         }, c.east) : false,
36510         center: Roo.apply({
36511             tabPosition: 'top',
36512             autoScroll:false,
36513             closeOnTab: true,
36514             titlebar:false,
36515             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36516         }, c.center)
36517     });
36518
36519     this.el.addClass('x-reader');
36520
36521     this.beginUpdate();
36522
36523     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36524         south: c.preview !== false ? Roo.apply({
36525             split:true,
36526             initialSize: 200,
36527             minSize: 100,
36528             autoScroll:true,
36529             collapsible:true,
36530             titlebar: true,
36531             cmargins:{top:5,left:0, right:0, bottom:0}
36532         }, c.preview) : false,
36533         center: Roo.apply({
36534             autoScroll:false,
36535             titlebar:false,
36536             minHeight:200
36537         }, c.listView)
36538     });
36539     this.add('center', new Roo.NestedLayoutPanel(inner,
36540             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36541
36542     this.endUpdate();
36543
36544     this.regions.preview = inner.getRegion('south');
36545     this.regions.listView = inner.getRegion('center');
36546 };
36547
36548 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
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 /**
36560  * @class Roo.grid.Grid
36561  * @extends Roo.util.Observable
36562  * This class represents the primary interface of a component based grid control.
36563  * <br><br>Usage:<pre><code>
36564  var grid = new Roo.grid.Grid("my-container-id", {
36565      ds: myDataStore,
36566      cm: myColModel,
36567      selModel: mySelectionModel,
36568      autoSizeColumns: true,
36569      monitorWindowResize: false,
36570      trackMouseOver: true
36571  });
36572  // set any options
36573  grid.render();
36574  * </code></pre>
36575  * <b>Common Problems:</b><br/>
36576  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36577  * element will correct this<br/>
36578  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36579  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36580  * are unpredictable.<br/>
36581  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36582  * grid to calculate dimensions/offsets.<br/>
36583   * @constructor
36584  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36585  * The container MUST have some type of size defined for the grid to fill. The container will be
36586  * automatically set to position relative if it isn't already.
36587  * @param {Object} config A config object that sets properties on this grid.
36588  */
36589 Roo.grid.Grid = function(container, config){
36590         // initialize the container
36591         this.container = Roo.get(container);
36592         this.container.update("");
36593         this.container.setStyle("overflow", "hidden");
36594     this.container.addClass('x-grid-container');
36595
36596     this.id = this.container.id;
36597
36598     Roo.apply(this, config);
36599     // check and correct shorthanded configs
36600     if(this.ds){
36601         this.dataSource = this.ds;
36602         delete this.ds;
36603     }
36604     if(this.cm){
36605         this.colModel = this.cm;
36606         delete this.cm;
36607     }
36608     if(this.sm){
36609         this.selModel = this.sm;
36610         delete this.sm;
36611     }
36612
36613     if (this.selModel) {
36614         this.selModel = Roo.factory(this.selModel, Roo.grid);
36615         this.sm = this.selModel;
36616         this.sm.xmodule = this.xmodule || false;
36617     }
36618     if (typeof(this.colModel.config) == 'undefined') {
36619         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36620         this.cm = this.colModel;
36621         this.cm.xmodule = this.xmodule || false;
36622     }
36623     if (this.dataSource) {
36624         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36625         this.ds = this.dataSource;
36626         this.ds.xmodule = this.xmodule || false;
36627          
36628     }
36629     
36630     
36631     
36632     if(this.width){
36633         this.container.setWidth(this.width);
36634     }
36635
36636     if(this.height){
36637         this.container.setHeight(this.height);
36638     }
36639     /** @private */
36640         this.addEvents({
36641         // raw events
36642         /**
36643          * @event click
36644          * The raw click event for the entire grid.
36645          * @param {Roo.EventObject} e
36646          */
36647         "click" : true,
36648         /**
36649          * @event dblclick
36650          * The raw dblclick event for the entire grid.
36651          * @param {Roo.EventObject} e
36652          */
36653         "dblclick" : true,
36654         /**
36655          * @event contextmenu
36656          * The raw contextmenu event for the entire grid.
36657          * @param {Roo.EventObject} e
36658          */
36659         "contextmenu" : true,
36660         /**
36661          * @event mousedown
36662          * The raw mousedown event for the entire grid.
36663          * @param {Roo.EventObject} e
36664          */
36665         "mousedown" : true,
36666         /**
36667          * @event mouseup
36668          * The raw mouseup event for the entire grid.
36669          * @param {Roo.EventObject} e
36670          */
36671         "mouseup" : true,
36672         /**
36673          * @event mouseover
36674          * The raw mouseover event for the entire grid.
36675          * @param {Roo.EventObject} e
36676          */
36677         "mouseover" : true,
36678         /**
36679          * @event mouseout
36680          * The raw mouseout event for the entire grid.
36681          * @param {Roo.EventObject} e
36682          */
36683         "mouseout" : true,
36684         /**
36685          * @event keypress
36686          * The raw keypress event for the entire grid.
36687          * @param {Roo.EventObject} e
36688          */
36689         "keypress" : true,
36690         /**
36691          * @event keydown
36692          * The raw keydown event for the entire grid.
36693          * @param {Roo.EventObject} e
36694          */
36695         "keydown" : true,
36696
36697         // custom events
36698
36699         /**
36700          * @event cellclick
36701          * Fires when a cell is clicked
36702          * @param {Grid} this
36703          * @param {Number} rowIndex
36704          * @param {Number} columnIndex
36705          * @param {Roo.EventObject} e
36706          */
36707         "cellclick" : true,
36708         /**
36709          * @event celldblclick
36710          * Fires when a cell is double clicked
36711          * @param {Grid} this
36712          * @param {Number} rowIndex
36713          * @param {Number} columnIndex
36714          * @param {Roo.EventObject} e
36715          */
36716         "celldblclick" : true,
36717         /**
36718          * @event rowclick
36719          * Fires when a row is clicked
36720          * @param {Grid} this
36721          * @param {Number} rowIndex
36722          * @param {Roo.EventObject} e
36723          */
36724         "rowclick" : true,
36725         /**
36726          * @event rowdblclick
36727          * Fires when a row is double clicked
36728          * @param {Grid} this
36729          * @param {Number} rowIndex
36730          * @param {Roo.EventObject} e
36731          */
36732         "rowdblclick" : true,
36733         /**
36734          * @event headerclick
36735          * Fires when a header is clicked
36736          * @param {Grid} this
36737          * @param {Number} columnIndex
36738          * @param {Roo.EventObject} e
36739          */
36740         "headerclick" : true,
36741         /**
36742          * @event headerdblclick
36743          * Fires when a header cell is double clicked
36744          * @param {Grid} this
36745          * @param {Number} columnIndex
36746          * @param {Roo.EventObject} e
36747          */
36748         "headerdblclick" : true,
36749         /**
36750          * @event rowcontextmenu
36751          * Fires when a row is right clicked
36752          * @param {Grid} this
36753          * @param {Number} rowIndex
36754          * @param {Roo.EventObject} e
36755          */
36756         "rowcontextmenu" : true,
36757         /**
36758          * @event cellcontextmenu
36759          * Fires when a cell is right clicked
36760          * @param {Grid} this
36761          * @param {Number} rowIndex
36762          * @param {Number} cellIndex
36763          * @param {Roo.EventObject} e
36764          */
36765          "cellcontextmenu" : true,
36766         /**
36767          * @event headercontextmenu
36768          * Fires when a header is right clicked
36769          * @param {Grid} this
36770          * @param {Number} columnIndex
36771          * @param {Roo.EventObject} e
36772          */
36773         "headercontextmenu" : true,
36774         /**
36775          * @event bodyscroll
36776          * Fires when the body element is scrolled
36777          * @param {Number} scrollLeft
36778          * @param {Number} scrollTop
36779          */
36780         "bodyscroll" : true,
36781         /**
36782          * @event columnresize
36783          * Fires when the user resizes a column
36784          * @param {Number} columnIndex
36785          * @param {Number} newSize
36786          */
36787         "columnresize" : true,
36788         /**
36789          * @event columnmove
36790          * Fires when the user moves a column
36791          * @param {Number} oldIndex
36792          * @param {Number} newIndex
36793          */
36794         "columnmove" : true,
36795         /**
36796          * @event startdrag
36797          * Fires when row(s) start being dragged
36798          * @param {Grid} this
36799          * @param {Roo.GridDD} dd The drag drop object
36800          * @param {event} e The raw browser event
36801          */
36802         "startdrag" : true,
36803         /**
36804          * @event enddrag
36805          * Fires when a drag operation is complete
36806          * @param {Grid} this
36807          * @param {Roo.GridDD} dd The drag drop object
36808          * @param {event} e The raw browser event
36809          */
36810         "enddrag" : true,
36811         /**
36812          * @event dragdrop
36813          * Fires when dragged row(s) are dropped on a valid DD target
36814          * @param {Grid} this
36815          * @param {Roo.GridDD} dd The drag drop object
36816          * @param {String} targetId The target drag drop object
36817          * @param {event} e The raw browser event
36818          */
36819         "dragdrop" : true,
36820         /**
36821          * @event dragover
36822          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36823          * @param {Grid} this
36824          * @param {Roo.GridDD} dd The drag drop object
36825          * @param {String} targetId The target drag drop object
36826          * @param {event} e The raw browser event
36827          */
36828         "dragover" : true,
36829         /**
36830          * @event dragenter
36831          *  Fires when the dragged row(s) first cross another DD target while being dragged
36832          * @param {Grid} this
36833          * @param {Roo.GridDD} dd The drag drop object
36834          * @param {String} targetId The target drag drop object
36835          * @param {event} e The raw browser event
36836          */
36837         "dragenter" : true,
36838         /**
36839          * @event dragout
36840          * Fires when the dragged row(s) leave another DD target while being dragged
36841          * @param {Grid} this
36842          * @param {Roo.GridDD} dd The drag drop object
36843          * @param {String} targetId The target drag drop object
36844          * @param {event} e The raw browser event
36845          */
36846         "dragout" : true,
36847         /**
36848          * @event rowclass
36849          * Fires when a row is rendered, so you can change add a style to it.
36850          * @param {GridView} gridview   The grid view
36851          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36852          */
36853         'rowclass' : true,
36854
36855         /**
36856          * @event render
36857          * Fires when the grid is rendered
36858          * @param {Grid} grid
36859          */
36860         'render' : true
36861     });
36862
36863     Roo.grid.Grid.superclass.constructor.call(this);
36864 };
36865 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36866     
36867     /**
36868          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
36869          */
36870         /**
36871          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
36872          */
36873         /**
36874          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
36875          */
36876         /**
36877          * @cfg {Roo.grid.Store} ds The data store for the grid
36878          */
36879         /**
36880          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
36881          */
36882         /**
36883      * @cfg {String} ddGroup - drag drop group.
36884      */
36885       /**
36886      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
36887      */
36888
36889     /**
36890      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36891      */
36892     minColumnWidth : 25,
36893
36894     /**
36895      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36896      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36897      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36898      */
36899     autoSizeColumns : false,
36900
36901     /**
36902      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36903      */
36904     autoSizeHeaders : true,
36905
36906     /**
36907      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36908      */
36909     monitorWindowResize : true,
36910
36911     /**
36912      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36913      * rows measured to get a columns size. Default is 0 (all rows).
36914      */
36915     maxRowsToMeasure : 0,
36916
36917     /**
36918      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36919      */
36920     trackMouseOver : true,
36921
36922     /**
36923     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36924     */
36925       /**
36926     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
36927     */
36928     
36929     /**
36930     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36931     */
36932     enableDragDrop : false,
36933     
36934     /**
36935     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36936     */
36937     enableColumnMove : true,
36938     
36939     /**
36940     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36941     */
36942     enableColumnHide : true,
36943     
36944     /**
36945     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36946     */
36947     enableRowHeightSync : false,
36948     
36949     /**
36950     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36951     */
36952     stripeRows : true,
36953     
36954     /**
36955     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36956     */
36957     autoHeight : false,
36958
36959     /**
36960      * @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.
36961      */
36962     autoExpandColumn : false,
36963
36964     /**
36965     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36966     * Default is 50.
36967     */
36968     autoExpandMin : 50,
36969
36970     /**
36971     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36972     */
36973     autoExpandMax : 1000,
36974
36975     /**
36976     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36977     */
36978     view : null,
36979
36980     /**
36981     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36982     */
36983     loadMask : false,
36984     /**
36985     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36986     */
36987     dropTarget: false,
36988      /**
36989     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
36990     */ 
36991     sortColMenu : false,
36992     
36993     // private
36994     rendered : false,
36995
36996     /**
36997     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36998     * of a fixed width. Default is false.
36999     */
37000     /**
37001     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37002     */
37003     
37004     
37005     /**
37006     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37007     * %0 is replaced with the number of selected rows.
37008     */
37009     ddText : "{0} selected row{1}",
37010     
37011     
37012     /**
37013      * Called once after all setup has been completed and the grid is ready to be rendered.
37014      * @return {Roo.grid.Grid} this
37015      */
37016     render : function()
37017     {
37018         var c = this.container;
37019         // try to detect autoHeight/width mode
37020         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37021             this.autoHeight = true;
37022         }
37023         var view = this.getView();
37024         view.init(this);
37025
37026         c.on("click", this.onClick, this);
37027         c.on("dblclick", this.onDblClick, this);
37028         c.on("contextmenu", this.onContextMenu, this);
37029         c.on("keydown", this.onKeyDown, this);
37030         if (Roo.isTouch) {
37031             c.on("touchstart", this.onTouchStart, this);
37032         }
37033
37034         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37035
37036         this.getSelectionModel().init(this);
37037
37038         view.render();
37039
37040         if(this.loadMask){
37041             this.loadMask = new Roo.LoadMask(this.container,
37042                     Roo.apply({store:this.dataSource}, this.loadMask));
37043         }
37044         
37045         
37046         if (this.toolbar && this.toolbar.xtype) {
37047             this.toolbar.container = this.getView().getHeaderPanel(true);
37048             this.toolbar = new Roo.Toolbar(this.toolbar);
37049         }
37050         if (this.footer && this.footer.xtype) {
37051             this.footer.dataSource = this.getDataSource();
37052             this.footer.container = this.getView().getFooterPanel(true);
37053             this.footer = Roo.factory(this.footer, Roo);
37054         }
37055         if (this.dropTarget && this.dropTarget.xtype) {
37056             delete this.dropTarget.xtype;
37057             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37058         }
37059         
37060         
37061         this.rendered = true;
37062         this.fireEvent('render', this);
37063         return this;
37064     },
37065
37066     /**
37067      * Reconfigures the grid to use a different Store and Column Model.
37068      * The View will be bound to the new objects and refreshed.
37069      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37070      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37071      */
37072     reconfigure : function(dataSource, colModel){
37073         if(this.loadMask){
37074             this.loadMask.destroy();
37075             this.loadMask = new Roo.LoadMask(this.container,
37076                     Roo.apply({store:dataSource}, this.loadMask));
37077         }
37078         this.view.bind(dataSource, colModel);
37079         this.dataSource = dataSource;
37080         this.colModel = colModel;
37081         this.view.refresh(true);
37082     },
37083     /**
37084      * addColumns
37085      * Add's a column, default at the end..
37086      
37087      * @param {int} position to add (default end)
37088      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
37089      */
37090     addColumns : function(pos, ar)
37091     {
37092         
37093         for (var i =0;i< ar.length;i++) {
37094             var cfg = ar[i];
37095             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37096             this.cm.lookup[cfg.id] = cfg;
37097         }
37098         
37099         
37100         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37101             pos = this.cm.config.length; //this.cm.config.push(cfg);
37102         } 
37103         pos = Math.max(0,pos);
37104         ar.unshift(0);
37105         ar.unshift(pos);
37106         this.cm.config.splice.apply(this.cm.config, ar);
37107         
37108         
37109         
37110         this.view.generateRules(this.cm);
37111         this.view.refresh(true);
37112         
37113     },
37114     
37115     
37116     
37117     
37118     // private
37119     onKeyDown : function(e){
37120         this.fireEvent("keydown", e);
37121     },
37122
37123     /**
37124      * Destroy this grid.
37125      * @param {Boolean} removeEl True to remove the element
37126      */
37127     destroy : function(removeEl, keepListeners){
37128         if(this.loadMask){
37129             this.loadMask.destroy();
37130         }
37131         var c = this.container;
37132         c.removeAllListeners();
37133         this.view.destroy();
37134         this.colModel.purgeListeners();
37135         if(!keepListeners){
37136             this.purgeListeners();
37137         }
37138         c.update("");
37139         if(removeEl === true){
37140             c.remove();
37141         }
37142     },
37143
37144     // private
37145     processEvent : function(name, e){
37146         // does this fire select???
37147         //Roo.log('grid:processEvent '  + name);
37148         
37149         if (name != 'touchstart' ) {
37150             this.fireEvent(name, e);    
37151         }
37152         
37153         var t = e.getTarget();
37154         var v = this.view;
37155         var header = v.findHeaderIndex(t);
37156         if(header !== false){
37157             var ename = name == 'touchstart' ? 'click' : name;
37158              
37159             this.fireEvent("header" + ename, this, header, e);
37160         }else{
37161             var row = v.findRowIndex(t);
37162             var cell = v.findCellIndex(t);
37163             if (name == 'touchstart') {
37164                 // first touch is always a click.
37165                 // hopefull this happens after selection is updated.?
37166                 name = false;
37167                 
37168                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37169                     var cs = this.selModel.getSelectedCell();
37170                     if (row == cs[0] && cell == cs[1]){
37171                         name = 'dblclick';
37172                     }
37173                 }
37174                 if (typeof(this.selModel.getSelections) != 'undefined') {
37175                     var cs = this.selModel.getSelections();
37176                     var ds = this.dataSource;
37177                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
37178                         name = 'dblclick';
37179                     }
37180                 }
37181                 if (!name) {
37182                     return;
37183                 }
37184             }
37185             
37186             
37187             if(row !== false){
37188                 this.fireEvent("row" + name, this, row, e);
37189                 if(cell !== false){
37190                     this.fireEvent("cell" + name, this, row, cell, e);
37191                 }
37192             }
37193         }
37194     },
37195
37196     // private
37197     onClick : function(e){
37198         this.processEvent("click", e);
37199     },
37200    // private
37201     onTouchStart : function(e){
37202         this.processEvent("touchstart", e);
37203     },
37204
37205     // private
37206     onContextMenu : function(e, t){
37207         this.processEvent("contextmenu", e);
37208     },
37209
37210     // private
37211     onDblClick : function(e){
37212         this.processEvent("dblclick", e);
37213     },
37214
37215     // private
37216     walkCells : function(row, col, step, fn, scope){
37217         var cm = this.colModel, clen = cm.getColumnCount();
37218         var ds = this.dataSource, rlen = ds.getCount(), first = true;
37219         if(step < 0){
37220             if(col < 0){
37221                 row--;
37222                 first = false;
37223             }
37224             while(row >= 0){
37225                 if(!first){
37226                     col = clen-1;
37227                 }
37228                 first = false;
37229                 while(col >= 0){
37230                     if(fn.call(scope || this, row, col, cm) === true){
37231                         return [row, col];
37232                     }
37233                     col--;
37234                 }
37235                 row--;
37236             }
37237         } else {
37238             if(col >= clen){
37239                 row++;
37240                 first = false;
37241             }
37242             while(row < rlen){
37243                 if(!first){
37244                     col = 0;
37245                 }
37246                 first = false;
37247                 while(col < clen){
37248                     if(fn.call(scope || this, row, col, cm) === true){
37249                         return [row, col];
37250                     }
37251                     col++;
37252                 }
37253                 row++;
37254             }
37255         }
37256         return null;
37257     },
37258
37259     // private
37260     getSelections : function(){
37261         return this.selModel.getSelections();
37262     },
37263
37264     /**
37265      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37266      * but if manual update is required this method will initiate it.
37267      */
37268     autoSize : function(){
37269         if(this.rendered){
37270             this.view.layout();
37271             if(this.view.adjustForScroll){
37272                 this.view.adjustForScroll();
37273             }
37274         }
37275     },
37276
37277     /**
37278      * Returns the grid's underlying element.
37279      * @return {Element} The element
37280      */
37281     getGridEl : function(){
37282         return this.container;
37283     },
37284
37285     // private for compatibility, overridden by editor grid
37286     stopEditing : function(){},
37287
37288     /**
37289      * Returns the grid's SelectionModel.
37290      * @return {SelectionModel}
37291      */
37292     getSelectionModel : function(){
37293         if(!this.selModel){
37294             this.selModel = new Roo.grid.RowSelectionModel();
37295         }
37296         return this.selModel;
37297     },
37298
37299     /**
37300      * Returns the grid's DataSource.
37301      * @return {DataSource}
37302      */
37303     getDataSource : function(){
37304         return this.dataSource;
37305     },
37306
37307     /**
37308      * Returns the grid's ColumnModel.
37309      * @return {ColumnModel}
37310      */
37311     getColumnModel : function(){
37312         return this.colModel;
37313     },
37314
37315     /**
37316      * Returns the grid's GridView object.
37317      * @return {GridView}
37318      */
37319     getView : function(){
37320         if(!this.view){
37321             this.view = new Roo.grid.GridView(this.viewConfig);
37322             this.relayEvents(this.view, [
37323                 "beforerowremoved", "beforerowsinserted",
37324                 "beforerefresh", "rowremoved",
37325                 "rowsinserted", "rowupdated" ,"refresh"
37326             ]);
37327         }
37328         return this.view;
37329     },
37330     /**
37331      * Called to get grid's drag proxy text, by default returns this.ddText.
37332      * Override this to put something different in the dragged text.
37333      * @return {String}
37334      */
37335     getDragDropText : function(){
37336         var count = this.selModel.getCount();
37337         return String.format(this.ddText, count, count == 1 ? '' : 's');
37338     }
37339 });
37340 /*
37341  * Based on:
37342  * Ext JS Library 1.1.1
37343  * Copyright(c) 2006-2007, Ext JS, LLC.
37344  *
37345  * Originally Released Under LGPL - original licence link has changed is not relivant.
37346  *
37347  * Fork - LGPL
37348  * <script type="text/javascript">
37349  */
37350  /**
37351  * @class Roo.grid.AbstractGridView
37352  * @extends Roo.util.Observable
37353  * @abstract
37354  * Abstract base class for grid Views
37355  * @constructor
37356  */
37357 Roo.grid.AbstractGridView = function(){
37358         this.grid = null;
37359         
37360         this.events = {
37361             "beforerowremoved" : true,
37362             "beforerowsinserted" : true,
37363             "beforerefresh" : true,
37364             "rowremoved" : true,
37365             "rowsinserted" : true,
37366             "rowupdated" : true,
37367             "refresh" : true
37368         };
37369     Roo.grid.AbstractGridView.superclass.constructor.call(this);
37370 };
37371
37372 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37373     rowClass : "x-grid-row",
37374     cellClass : "x-grid-cell",
37375     tdClass : "x-grid-td",
37376     hdClass : "x-grid-hd",
37377     splitClass : "x-grid-hd-split",
37378     
37379     init: function(grid){
37380         this.grid = grid;
37381                 var cid = this.grid.getGridEl().id;
37382         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37383         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37384         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37385         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37386         },
37387         
37388     getColumnRenderers : function(){
37389         var renderers = [];
37390         var cm = this.grid.colModel;
37391         var colCount = cm.getColumnCount();
37392         for(var i = 0; i < colCount; i++){
37393             renderers[i] = cm.getRenderer(i);
37394         }
37395         return renderers;
37396     },
37397     
37398     getColumnIds : function(){
37399         var ids = [];
37400         var cm = this.grid.colModel;
37401         var colCount = cm.getColumnCount();
37402         for(var i = 0; i < colCount; i++){
37403             ids[i] = cm.getColumnId(i);
37404         }
37405         return ids;
37406     },
37407     
37408     getDataIndexes : function(){
37409         if(!this.indexMap){
37410             this.indexMap = this.buildIndexMap();
37411         }
37412         return this.indexMap.colToData;
37413     },
37414     
37415     getColumnIndexByDataIndex : function(dataIndex){
37416         if(!this.indexMap){
37417             this.indexMap = this.buildIndexMap();
37418         }
37419         return this.indexMap.dataToCol[dataIndex];
37420     },
37421     
37422     /**
37423      * Set a css style for a column dynamically. 
37424      * @param {Number} colIndex The index of the column
37425      * @param {String} name The css property name
37426      * @param {String} value The css value
37427      */
37428     setCSSStyle : function(colIndex, name, value){
37429         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37430         Roo.util.CSS.updateRule(selector, name, value);
37431     },
37432     
37433     generateRules : function(cm){
37434         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37435         Roo.util.CSS.removeStyleSheet(rulesId);
37436         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37437             var cid = cm.getColumnId(i);
37438             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37439                          this.tdSelector, cid, " {\n}\n",
37440                          this.hdSelector, cid, " {\n}\n",
37441                          this.splitSelector, cid, " {\n}\n");
37442         }
37443         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37444     }
37445 });/*
37446  * Based on:
37447  * Ext JS Library 1.1.1
37448  * Copyright(c) 2006-2007, Ext JS, LLC.
37449  *
37450  * Originally Released Under LGPL - original licence link has changed is not relivant.
37451  *
37452  * Fork - LGPL
37453  * <script type="text/javascript">
37454  */
37455
37456 // private
37457 // This is a support class used internally by the Grid components
37458 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37459     this.grid = grid;
37460     this.view = grid.getView();
37461     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37462     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37463     if(hd2){
37464         this.setHandleElId(Roo.id(hd));
37465         this.setOuterHandleElId(Roo.id(hd2));
37466     }
37467     this.scroll = false;
37468 };
37469 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37470     maxDragWidth: 120,
37471     getDragData : function(e){
37472         var t = Roo.lib.Event.getTarget(e);
37473         var h = this.view.findHeaderCell(t);
37474         if(h){
37475             return {ddel: h.firstChild, header:h};
37476         }
37477         return false;
37478     },
37479
37480     onInitDrag : function(e){
37481         this.view.headersDisabled = true;
37482         var clone = this.dragData.ddel.cloneNode(true);
37483         clone.id = Roo.id();
37484         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37485         this.proxy.update(clone);
37486         return true;
37487     },
37488
37489     afterValidDrop : function(){
37490         var v = this.view;
37491         setTimeout(function(){
37492             v.headersDisabled = false;
37493         }, 50);
37494     },
37495
37496     afterInvalidDrop : function(){
37497         var v = this.view;
37498         setTimeout(function(){
37499             v.headersDisabled = false;
37500         }, 50);
37501     }
37502 });
37503 /*
37504  * Based on:
37505  * Ext JS Library 1.1.1
37506  * Copyright(c) 2006-2007, Ext JS, LLC.
37507  *
37508  * Originally Released Under LGPL - original licence link has changed is not relivant.
37509  *
37510  * Fork - LGPL
37511  * <script type="text/javascript">
37512  */
37513 // private
37514 // This is a support class used internally by the Grid components
37515 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37516     this.grid = grid;
37517     this.view = grid.getView();
37518     // split the proxies so they don't interfere with mouse events
37519     this.proxyTop = Roo.DomHelper.append(document.body, {
37520         cls:"col-move-top", html:"&#160;"
37521     }, true);
37522     this.proxyBottom = Roo.DomHelper.append(document.body, {
37523         cls:"col-move-bottom", html:"&#160;"
37524     }, true);
37525     this.proxyTop.hide = this.proxyBottom.hide = function(){
37526         this.setLeftTop(-100,-100);
37527         this.setStyle("visibility", "hidden");
37528     };
37529     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37530     // temporarily disabled
37531     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37532     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37533 };
37534 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37535     proxyOffsets : [-4, -9],
37536     fly: Roo.Element.fly,
37537
37538     getTargetFromEvent : function(e){
37539         var t = Roo.lib.Event.getTarget(e);
37540         var cindex = this.view.findCellIndex(t);
37541         if(cindex !== false){
37542             return this.view.getHeaderCell(cindex);
37543         }
37544         return null;
37545     },
37546
37547     nextVisible : function(h){
37548         var v = this.view, cm = this.grid.colModel;
37549         h = h.nextSibling;
37550         while(h){
37551             if(!cm.isHidden(v.getCellIndex(h))){
37552                 return h;
37553             }
37554             h = h.nextSibling;
37555         }
37556         return null;
37557     },
37558
37559     prevVisible : function(h){
37560         var v = this.view, cm = this.grid.colModel;
37561         h = h.prevSibling;
37562         while(h){
37563             if(!cm.isHidden(v.getCellIndex(h))){
37564                 return h;
37565             }
37566             h = h.prevSibling;
37567         }
37568         return null;
37569     },
37570
37571     positionIndicator : function(h, n, e){
37572         var x = Roo.lib.Event.getPageX(e);
37573         var r = Roo.lib.Dom.getRegion(n.firstChild);
37574         var px, pt, py = r.top + this.proxyOffsets[1];
37575         if((r.right - x) <= (r.right-r.left)/2){
37576             px = r.right+this.view.borderWidth;
37577             pt = "after";
37578         }else{
37579             px = r.left;
37580             pt = "before";
37581         }
37582         var oldIndex = this.view.getCellIndex(h);
37583         var newIndex = this.view.getCellIndex(n);
37584
37585         if(this.grid.colModel.isFixed(newIndex)){
37586             return false;
37587         }
37588
37589         var locked = this.grid.colModel.isLocked(newIndex);
37590
37591         if(pt == "after"){
37592             newIndex++;
37593         }
37594         if(oldIndex < newIndex){
37595             newIndex--;
37596         }
37597         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37598             return false;
37599         }
37600         px +=  this.proxyOffsets[0];
37601         this.proxyTop.setLeftTop(px, py);
37602         this.proxyTop.show();
37603         if(!this.bottomOffset){
37604             this.bottomOffset = this.view.mainHd.getHeight();
37605         }
37606         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37607         this.proxyBottom.show();
37608         return pt;
37609     },
37610
37611     onNodeEnter : function(n, dd, e, data){
37612         if(data.header != n){
37613             this.positionIndicator(data.header, n, e);
37614         }
37615     },
37616
37617     onNodeOver : function(n, dd, e, data){
37618         var result = false;
37619         if(data.header != n){
37620             result = this.positionIndicator(data.header, n, e);
37621         }
37622         if(!result){
37623             this.proxyTop.hide();
37624             this.proxyBottom.hide();
37625         }
37626         return result ? this.dropAllowed : this.dropNotAllowed;
37627     },
37628
37629     onNodeOut : function(n, dd, e, data){
37630         this.proxyTop.hide();
37631         this.proxyBottom.hide();
37632     },
37633
37634     onNodeDrop : function(n, dd, e, data){
37635         var h = data.header;
37636         if(h != n){
37637             var cm = this.grid.colModel;
37638             var x = Roo.lib.Event.getPageX(e);
37639             var r = Roo.lib.Dom.getRegion(n.firstChild);
37640             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37641             var oldIndex = this.view.getCellIndex(h);
37642             var newIndex = this.view.getCellIndex(n);
37643             var locked = cm.isLocked(newIndex);
37644             if(pt == "after"){
37645                 newIndex++;
37646             }
37647             if(oldIndex < newIndex){
37648                 newIndex--;
37649             }
37650             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37651                 return false;
37652             }
37653             cm.setLocked(oldIndex, locked, true);
37654             cm.moveColumn(oldIndex, newIndex);
37655             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37656             return true;
37657         }
37658         return false;
37659     }
37660 });
37661 /*
37662  * Based on:
37663  * Ext JS Library 1.1.1
37664  * Copyright(c) 2006-2007, Ext JS, LLC.
37665  *
37666  * Originally Released Under LGPL - original licence link has changed is not relivant.
37667  *
37668  * Fork - LGPL
37669  * <script type="text/javascript">
37670  */
37671   
37672 /**
37673  * @class Roo.grid.GridView
37674  * @extends Roo.util.Observable
37675  *
37676  * @constructor
37677  * @param {Object} config
37678  */
37679 Roo.grid.GridView = function(config){
37680     Roo.grid.GridView.superclass.constructor.call(this);
37681     this.el = null;
37682
37683     Roo.apply(this, config);
37684 };
37685
37686 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37687
37688     unselectable :  'unselectable="on"',
37689     unselectableCls :  'x-unselectable',
37690     
37691     
37692     rowClass : "x-grid-row",
37693
37694     cellClass : "x-grid-col",
37695
37696     tdClass : "x-grid-td",
37697
37698     hdClass : "x-grid-hd",
37699
37700     splitClass : "x-grid-split",
37701
37702     sortClasses : ["sort-asc", "sort-desc"],
37703
37704     enableMoveAnim : false,
37705
37706     hlColor: "C3DAF9",
37707
37708     dh : Roo.DomHelper,
37709
37710     fly : Roo.Element.fly,
37711
37712     css : Roo.util.CSS,
37713
37714     borderWidth: 1,
37715
37716     splitOffset: 3,
37717
37718     scrollIncrement : 22,
37719
37720     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37721
37722     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37723
37724     bind : function(ds, cm){
37725         if(this.ds){
37726             this.ds.un("load", this.onLoad, this);
37727             this.ds.un("datachanged", this.onDataChange, this);
37728             this.ds.un("add", this.onAdd, this);
37729             this.ds.un("remove", this.onRemove, this);
37730             this.ds.un("update", this.onUpdate, this);
37731             this.ds.un("clear", this.onClear, this);
37732         }
37733         if(ds){
37734             ds.on("load", this.onLoad, this);
37735             ds.on("datachanged", this.onDataChange, this);
37736             ds.on("add", this.onAdd, this);
37737             ds.on("remove", this.onRemove, this);
37738             ds.on("update", this.onUpdate, this);
37739             ds.on("clear", this.onClear, this);
37740         }
37741         this.ds = ds;
37742
37743         if(this.cm){
37744             this.cm.un("widthchange", this.onColWidthChange, this);
37745             this.cm.un("headerchange", this.onHeaderChange, this);
37746             this.cm.un("hiddenchange", this.onHiddenChange, this);
37747             this.cm.un("columnmoved", this.onColumnMove, this);
37748             this.cm.un("columnlockchange", this.onColumnLock, this);
37749         }
37750         if(cm){
37751             this.generateRules(cm);
37752             cm.on("widthchange", this.onColWidthChange, this);
37753             cm.on("headerchange", this.onHeaderChange, this);
37754             cm.on("hiddenchange", this.onHiddenChange, this);
37755             cm.on("columnmoved", this.onColumnMove, this);
37756             cm.on("columnlockchange", this.onColumnLock, this);
37757         }
37758         this.cm = cm;
37759     },
37760
37761     init: function(grid){
37762         Roo.grid.GridView.superclass.init.call(this, grid);
37763
37764         this.bind(grid.dataSource, grid.colModel);
37765
37766         grid.on("headerclick", this.handleHeaderClick, this);
37767
37768         if(grid.trackMouseOver){
37769             grid.on("mouseover", this.onRowOver, this);
37770             grid.on("mouseout", this.onRowOut, this);
37771         }
37772         grid.cancelTextSelection = function(){};
37773         this.gridId = grid.id;
37774
37775         var tpls = this.templates || {};
37776
37777         if(!tpls.master){
37778             tpls.master = new Roo.Template(
37779                '<div class="x-grid" hidefocus="true">',
37780                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37781                   '<div class="x-grid-topbar"></div>',
37782                   '<div class="x-grid-scroller"><div></div></div>',
37783                   '<div class="x-grid-locked">',
37784                       '<div class="x-grid-header">{lockedHeader}</div>',
37785                       '<div class="x-grid-body">{lockedBody}</div>',
37786                   "</div>",
37787                   '<div class="x-grid-viewport">',
37788                       '<div class="x-grid-header">{header}</div>',
37789                       '<div class="x-grid-body">{body}</div>',
37790                   "</div>",
37791                   '<div class="x-grid-bottombar"></div>',
37792                  
37793                   '<div class="x-grid-resize-proxy">&#160;</div>',
37794                "</div>"
37795             );
37796             tpls.master.disableformats = true;
37797         }
37798
37799         if(!tpls.header){
37800             tpls.header = new Roo.Template(
37801                '<table border="0" cellspacing="0" cellpadding="0">',
37802                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37803                "</table>{splits}"
37804             );
37805             tpls.header.disableformats = true;
37806         }
37807         tpls.header.compile();
37808
37809         if(!tpls.hcell){
37810             tpls.hcell = new Roo.Template(
37811                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37812                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37813                 "</div></td>"
37814              );
37815              tpls.hcell.disableFormats = true;
37816         }
37817         tpls.hcell.compile();
37818
37819         if(!tpls.hsplit){
37820             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37821                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37822             tpls.hsplit.disableFormats = true;
37823         }
37824         tpls.hsplit.compile();
37825
37826         if(!tpls.body){
37827             tpls.body = new Roo.Template(
37828                '<table border="0" cellspacing="0" cellpadding="0">',
37829                "<tbody>{rows}</tbody>",
37830                "</table>"
37831             );
37832             tpls.body.disableFormats = true;
37833         }
37834         tpls.body.compile();
37835
37836         if(!tpls.row){
37837             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37838             tpls.row.disableFormats = true;
37839         }
37840         tpls.row.compile();
37841
37842         if(!tpls.cell){
37843             tpls.cell = new Roo.Template(
37844                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37845                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37846                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37847                 "</td>"
37848             );
37849             tpls.cell.disableFormats = true;
37850         }
37851         tpls.cell.compile();
37852
37853         this.templates = tpls;
37854     },
37855
37856     // remap these for backwards compat
37857     onColWidthChange : function(){
37858         this.updateColumns.apply(this, arguments);
37859     },
37860     onHeaderChange : function(){
37861         this.updateHeaders.apply(this, arguments);
37862     }, 
37863     onHiddenChange : function(){
37864         this.handleHiddenChange.apply(this, arguments);
37865     },
37866     onColumnMove : function(){
37867         this.handleColumnMove.apply(this, arguments);
37868     },
37869     onColumnLock : function(){
37870         this.handleLockChange.apply(this, arguments);
37871     },
37872
37873     onDataChange : function(){
37874         this.refresh();
37875         this.updateHeaderSortState();
37876     },
37877
37878     onClear : function(){
37879         this.refresh();
37880     },
37881
37882     onUpdate : function(ds, record){
37883         this.refreshRow(record);
37884     },
37885
37886     refreshRow : function(record){
37887         var ds = this.ds, index;
37888         if(typeof record == 'number'){
37889             index = record;
37890             record = ds.getAt(index);
37891         }else{
37892             index = ds.indexOf(record);
37893         }
37894         this.insertRows(ds, index, index, true);
37895         this.onRemove(ds, record, index+1, true);
37896         this.syncRowHeights(index, index);
37897         this.layout();
37898         this.fireEvent("rowupdated", this, index, record);
37899     },
37900
37901     onAdd : function(ds, records, index){
37902         this.insertRows(ds, index, index + (records.length-1));
37903     },
37904
37905     onRemove : function(ds, record, index, isUpdate){
37906         if(isUpdate !== true){
37907             this.fireEvent("beforerowremoved", this, index, record);
37908         }
37909         var bt = this.getBodyTable(), lt = this.getLockedTable();
37910         if(bt.rows[index]){
37911             bt.firstChild.removeChild(bt.rows[index]);
37912         }
37913         if(lt.rows[index]){
37914             lt.firstChild.removeChild(lt.rows[index]);
37915         }
37916         if(isUpdate !== true){
37917             this.stripeRows(index);
37918             this.syncRowHeights(index, index);
37919             this.layout();
37920             this.fireEvent("rowremoved", this, index, record);
37921         }
37922     },
37923
37924     onLoad : function(){
37925         this.scrollToTop();
37926     },
37927
37928     /**
37929      * Scrolls the grid to the top
37930      */
37931     scrollToTop : function(){
37932         if(this.scroller){
37933             this.scroller.dom.scrollTop = 0;
37934             this.syncScroll();
37935         }
37936     },
37937
37938     /**
37939      * Gets a panel in the header of the grid that can be used for toolbars etc.
37940      * After modifying the contents of this panel a call to grid.autoSize() may be
37941      * required to register any changes in size.
37942      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37943      * @return Roo.Element
37944      */
37945     getHeaderPanel : function(doShow){
37946         if(doShow){
37947             this.headerPanel.show();
37948         }
37949         return this.headerPanel;
37950     },
37951
37952     /**
37953      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37954      * After modifying the contents of this panel a call to grid.autoSize() may be
37955      * required to register any changes in size.
37956      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37957      * @return Roo.Element
37958      */
37959     getFooterPanel : function(doShow){
37960         if(doShow){
37961             this.footerPanel.show();
37962         }
37963         return this.footerPanel;
37964     },
37965
37966     initElements : function(){
37967         var E = Roo.Element;
37968         var el = this.grid.getGridEl().dom.firstChild;
37969         var cs = el.childNodes;
37970
37971         this.el = new E(el);
37972         
37973          this.focusEl = new E(el.firstChild);
37974         this.focusEl.swallowEvent("click", true);
37975         
37976         this.headerPanel = new E(cs[1]);
37977         this.headerPanel.enableDisplayMode("block");
37978
37979         this.scroller = new E(cs[2]);
37980         this.scrollSizer = new E(this.scroller.dom.firstChild);
37981
37982         this.lockedWrap = new E(cs[3]);
37983         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37984         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37985
37986         this.mainWrap = new E(cs[4]);
37987         this.mainHd = new E(this.mainWrap.dom.firstChild);
37988         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37989
37990         this.footerPanel = new E(cs[5]);
37991         this.footerPanel.enableDisplayMode("block");
37992
37993         this.resizeProxy = new E(cs[6]);
37994
37995         this.headerSelector = String.format(
37996            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37997            this.lockedHd.id, this.mainHd.id
37998         );
37999
38000         this.splitterSelector = String.format(
38001            '#{0} div.x-grid-split, #{1} div.x-grid-split',
38002            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38003         );
38004     },
38005     idToCssName : function(s)
38006     {
38007         return s.replace(/[^a-z0-9]+/ig, '-');
38008     },
38009
38010     getHeaderCell : function(index){
38011         return Roo.DomQuery.select(this.headerSelector)[index];
38012     },
38013
38014     getHeaderCellMeasure : function(index){
38015         return this.getHeaderCell(index).firstChild;
38016     },
38017
38018     getHeaderCellText : function(index){
38019         return this.getHeaderCell(index).firstChild.firstChild;
38020     },
38021
38022     getLockedTable : function(){
38023         return this.lockedBody.dom.firstChild;
38024     },
38025
38026     getBodyTable : function(){
38027         return this.mainBody.dom.firstChild;
38028     },
38029
38030     getLockedRow : function(index){
38031         return this.getLockedTable().rows[index];
38032     },
38033
38034     getRow : function(index){
38035         return this.getBodyTable().rows[index];
38036     },
38037
38038     getRowComposite : function(index){
38039         if(!this.rowEl){
38040             this.rowEl = new Roo.CompositeElementLite();
38041         }
38042         var els = [], lrow, mrow;
38043         if(lrow = this.getLockedRow(index)){
38044             els.push(lrow);
38045         }
38046         if(mrow = this.getRow(index)){
38047             els.push(mrow);
38048         }
38049         this.rowEl.elements = els;
38050         return this.rowEl;
38051     },
38052     /**
38053      * Gets the 'td' of the cell
38054      * 
38055      * @param {Integer} rowIndex row to select
38056      * @param {Integer} colIndex column to select
38057      * 
38058      * @return {Object} 
38059      */
38060     getCell : function(rowIndex, colIndex){
38061         var locked = this.cm.getLockedCount();
38062         var source;
38063         if(colIndex < locked){
38064             source = this.lockedBody.dom.firstChild;
38065         }else{
38066             source = this.mainBody.dom.firstChild;
38067             colIndex -= locked;
38068         }
38069         return source.rows[rowIndex].childNodes[colIndex];
38070     },
38071
38072     getCellText : function(rowIndex, colIndex){
38073         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38074     },
38075
38076     getCellBox : function(cell){
38077         var b = this.fly(cell).getBox();
38078         if(Roo.isOpera){ // opera fails to report the Y
38079             b.y = cell.offsetTop + this.mainBody.getY();
38080         }
38081         return b;
38082     },
38083
38084     getCellIndex : function(cell){
38085         var id = String(cell.className).match(this.cellRE);
38086         if(id){
38087             return parseInt(id[1], 10);
38088         }
38089         return 0;
38090     },
38091
38092     findHeaderIndex : function(n){
38093         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38094         return r ? this.getCellIndex(r) : false;
38095     },
38096
38097     findHeaderCell : function(n){
38098         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38099         return r ? r : false;
38100     },
38101
38102     findRowIndex : function(n){
38103         if(!n){
38104             return false;
38105         }
38106         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38107         return r ? r.rowIndex : false;
38108     },
38109
38110     findCellIndex : function(node){
38111         var stop = this.el.dom;
38112         while(node && node != stop){
38113             if(this.findRE.test(node.className)){
38114                 return this.getCellIndex(node);
38115             }
38116             node = node.parentNode;
38117         }
38118         return false;
38119     },
38120
38121     getColumnId : function(index){
38122         return this.cm.getColumnId(index);
38123     },
38124
38125     getSplitters : function()
38126     {
38127         if(this.splitterSelector){
38128            return Roo.DomQuery.select(this.splitterSelector);
38129         }else{
38130             return null;
38131       }
38132     },
38133
38134     getSplitter : function(index){
38135         return this.getSplitters()[index];
38136     },
38137
38138     onRowOver : function(e, t){
38139         var row;
38140         if((row = this.findRowIndex(t)) !== false){
38141             this.getRowComposite(row).addClass("x-grid-row-over");
38142         }
38143     },
38144
38145     onRowOut : function(e, t){
38146         var row;
38147         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38148             this.getRowComposite(row).removeClass("x-grid-row-over");
38149         }
38150     },
38151
38152     renderHeaders : function(){
38153         var cm = this.cm;
38154         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38155         var cb = [], lb = [], sb = [], lsb = [], p = {};
38156         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38157             p.cellId = "x-grid-hd-0-" + i;
38158             p.splitId = "x-grid-csplit-0-" + i;
38159             p.id = cm.getColumnId(i);
38160             p.value = cm.getColumnHeader(i) || "";
38161             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
38162             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38163             if(!cm.isLocked(i)){
38164                 cb[cb.length] = ct.apply(p);
38165                 sb[sb.length] = st.apply(p);
38166             }else{
38167                 lb[lb.length] = ct.apply(p);
38168                 lsb[lsb.length] = st.apply(p);
38169             }
38170         }
38171         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38172                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38173     },
38174
38175     updateHeaders : function(){
38176         var html = this.renderHeaders();
38177         this.lockedHd.update(html[0]);
38178         this.mainHd.update(html[1]);
38179     },
38180
38181     /**
38182      * Focuses the specified row.
38183      * @param {Number} row The row index
38184      */
38185     focusRow : function(row)
38186     {
38187         //Roo.log('GridView.focusRow');
38188         var x = this.scroller.dom.scrollLeft;
38189         this.focusCell(row, 0, false);
38190         this.scroller.dom.scrollLeft = x;
38191     },
38192
38193     /**
38194      * Focuses the specified cell.
38195      * @param {Number} row The row index
38196      * @param {Number} col The column index
38197      * @param {Boolean} hscroll false to disable horizontal scrolling
38198      */
38199     focusCell : function(row, col, hscroll)
38200     {
38201         //Roo.log('GridView.focusCell');
38202         var el = this.ensureVisible(row, col, hscroll);
38203         this.focusEl.alignTo(el, "tl-tl");
38204         if(Roo.isGecko){
38205             this.focusEl.focus();
38206         }else{
38207             this.focusEl.focus.defer(1, this.focusEl);
38208         }
38209     },
38210
38211     /**
38212      * Scrolls the specified cell into view
38213      * @param {Number} row The row index
38214      * @param {Number} col The column index
38215      * @param {Boolean} hscroll false to disable horizontal scrolling
38216      */
38217     ensureVisible : function(row, col, hscroll)
38218     {
38219         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38220         //return null; //disable for testing.
38221         if(typeof row != "number"){
38222             row = row.rowIndex;
38223         }
38224         if(row < 0 && row >= this.ds.getCount()){
38225             return  null;
38226         }
38227         col = (col !== undefined ? col : 0);
38228         var cm = this.grid.colModel;
38229         while(cm.isHidden(col)){
38230             col++;
38231         }
38232
38233         var el = this.getCell(row, col);
38234         if(!el){
38235             return null;
38236         }
38237         var c = this.scroller.dom;
38238
38239         var ctop = parseInt(el.offsetTop, 10);
38240         var cleft = parseInt(el.offsetLeft, 10);
38241         var cbot = ctop + el.offsetHeight;
38242         var cright = cleft + el.offsetWidth;
38243         
38244         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38245         var stop = parseInt(c.scrollTop, 10);
38246         var sleft = parseInt(c.scrollLeft, 10);
38247         var sbot = stop + ch;
38248         var sright = sleft + c.clientWidth;
38249         /*
38250         Roo.log('GridView.ensureVisible:' +
38251                 ' ctop:' + ctop +
38252                 ' c.clientHeight:' + c.clientHeight +
38253                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38254                 ' stop:' + stop +
38255                 ' cbot:' + cbot +
38256                 ' sbot:' + sbot +
38257                 ' ch:' + ch  
38258                 );
38259         */
38260         if(ctop < stop){
38261             c.scrollTop = ctop;
38262             //Roo.log("set scrolltop to ctop DISABLE?");
38263         }else if(cbot > sbot){
38264             //Roo.log("set scrolltop to cbot-ch");
38265             c.scrollTop = cbot-ch;
38266         }
38267         
38268         if(hscroll !== false){
38269             if(cleft < sleft){
38270                 c.scrollLeft = cleft;
38271             }else if(cright > sright){
38272                 c.scrollLeft = cright-c.clientWidth;
38273             }
38274         }
38275          
38276         return el;
38277     },
38278
38279     updateColumns : function(){
38280         this.grid.stopEditing();
38281         var cm = this.grid.colModel, colIds = this.getColumnIds();
38282         //var totalWidth = cm.getTotalWidth();
38283         var pos = 0;
38284         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38285             //if(cm.isHidden(i)) continue;
38286             var w = cm.getColumnWidth(i);
38287             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38288             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38289         }
38290         this.updateSplitters();
38291     },
38292
38293     generateRules : function(cm){
38294         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38295         Roo.util.CSS.removeStyleSheet(rulesId);
38296         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38297             var cid = cm.getColumnId(i);
38298             var align = '';
38299             if(cm.config[i].align){
38300                 align = 'text-align:'+cm.config[i].align+';';
38301             }
38302             var hidden = '';
38303             if(cm.isHidden(i)){
38304                 hidden = 'display:none;';
38305             }
38306             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38307             ruleBuf.push(
38308                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38309                     this.hdSelector, cid, " {\n", align, width, "}\n",
38310                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
38311                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
38312         }
38313         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38314     },
38315
38316     updateSplitters : function(){
38317         var cm = this.cm, s = this.getSplitters();
38318         if(s){ // splitters not created yet
38319             var pos = 0, locked = true;
38320             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38321                 if(cm.isHidden(i)) {
38322                     continue;
38323                 }
38324                 var w = cm.getColumnWidth(i); // make sure it's a number
38325                 if(!cm.isLocked(i) && locked){
38326                     pos = 0;
38327                     locked = false;
38328                 }
38329                 pos += w;
38330                 s[i].style.left = (pos-this.splitOffset) + "px";
38331             }
38332         }
38333     },
38334
38335     handleHiddenChange : function(colModel, colIndex, hidden){
38336         if(hidden){
38337             this.hideColumn(colIndex);
38338         }else{
38339             this.unhideColumn(colIndex);
38340         }
38341     },
38342
38343     hideColumn : function(colIndex){
38344         var cid = this.getColumnId(colIndex);
38345         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38346         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38347         if(Roo.isSafari){
38348             this.updateHeaders();
38349         }
38350         this.updateSplitters();
38351         this.layout();
38352     },
38353
38354     unhideColumn : function(colIndex){
38355         var cid = this.getColumnId(colIndex);
38356         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38357         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38358
38359         if(Roo.isSafari){
38360             this.updateHeaders();
38361         }
38362         this.updateSplitters();
38363         this.layout();
38364     },
38365
38366     insertRows : function(dm, firstRow, lastRow, isUpdate){
38367         if(firstRow == 0 && lastRow == dm.getCount()-1){
38368             this.refresh();
38369         }else{
38370             if(!isUpdate){
38371                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38372             }
38373             var s = this.getScrollState();
38374             var markup = this.renderRows(firstRow, lastRow);
38375             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38376             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38377             this.restoreScroll(s);
38378             if(!isUpdate){
38379                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38380                 this.syncRowHeights(firstRow, lastRow);
38381                 this.stripeRows(firstRow);
38382                 this.layout();
38383             }
38384         }
38385     },
38386
38387     bufferRows : function(markup, target, index){
38388         var before = null, trows = target.rows, tbody = target.tBodies[0];
38389         if(index < trows.length){
38390             before = trows[index];
38391         }
38392         var b = document.createElement("div");
38393         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38394         var rows = b.firstChild.rows;
38395         for(var i = 0, len = rows.length; i < len; i++){
38396             if(before){
38397                 tbody.insertBefore(rows[0], before);
38398             }else{
38399                 tbody.appendChild(rows[0]);
38400             }
38401         }
38402         b.innerHTML = "";
38403         b = null;
38404     },
38405
38406     deleteRows : function(dm, firstRow, lastRow){
38407         if(dm.getRowCount()<1){
38408             this.fireEvent("beforerefresh", this);
38409             this.mainBody.update("");
38410             this.lockedBody.update("");
38411             this.fireEvent("refresh", this);
38412         }else{
38413             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38414             var bt = this.getBodyTable();
38415             var tbody = bt.firstChild;
38416             var rows = bt.rows;
38417             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38418                 tbody.removeChild(rows[firstRow]);
38419             }
38420             this.stripeRows(firstRow);
38421             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38422         }
38423     },
38424
38425     updateRows : function(dataSource, firstRow, lastRow){
38426         var s = this.getScrollState();
38427         this.refresh();
38428         this.restoreScroll(s);
38429     },
38430
38431     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38432         if(!noRefresh){
38433            this.refresh();
38434         }
38435         this.updateHeaderSortState();
38436     },
38437
38438     getScrollState : function(){
38439         
38440         var sb = this.scroller.dom;
38441         return {left: sb.scrollLeft, top: sb.scrollTop};
38442     },
38443
38444     stripeRows : function(startRow){
38445         if(!this.grid.stripeRows || this.ds.getCount() < 1){
38446             return;
38447         }
38448         startRow = startRow || 0;
38449         var rows = this.getBodyTable().rows;
38450         var lrows = this.getLockedTable().rows;
38451         var cls = ' x-grid-row-alt ';
38452         for(var i = startRow, len = rows.length; i < len; i++){
38453             var row = rows[i], lrow = lrows[i];
38454             var isAlt = ((i+1) % 2 == 0);
38455             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38456             if(isAlt == hasAlt){
38457                 continue;
38458             }
38459             if(isAlt){
38460                 row.className += " x-grid-row-alt";
38461             }else{
38462                 row.className = row.className.replace("x-grid-row-alt", "");
38463             }
38464             if(lrow){
38465                 lrow.className = row.className;
38466             }
38467         }
38468     },
38469
38470     restoreScroll : function(state){
38471         //Roo.log('GridView.restoreScroll');
38472         var sb = this.scroller.dom;
38473         sb.scrollLeft = state.left;
38474         sb.scrollTop = state.top;
38475         this.syncScroll();
38476     },
38477
38478     syncScroll : function(){
38479         //Roo.log('GridView.syncScroll');
38480         var sb = this.scroller.dom;
38481         var sh = this.mainHd.dom;
38482         var bs = this.mainBody.dom;
38483         var lv = this.lockedBody.dom;
38484         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38485         lv.scrollTop = bs.scrollTop = sb.scrollTop;
38486     },
38487
38488     handleScroll : function(e){
38489         this.syncScroll();
38490         var sb = this.scroller.dom;
38491         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38492         e.stopEvent();
38493     },
38494
38495     handleWheel : function(e){
38496         var d = e.getWheelDelta();
38497         this.scroller.dom.scrollTop -= d*22;
38498         // set this here to prevent jumpy scrolling on large tables
38499         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38500         e.stopEvent();
38501     },
38502
38503     renderRows : function(startRow, endRow){
38504         // pull in all the crap needed to render rows
38505         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38506         var colCount = cm.getColumnCount();
38507
38508         if(ds.getCount() < 1){
38509             return ["", ""];
38510         }
38511
38512         // build a map for all the columns
38513         var cs = [];
38514         for(var i = 0; i < colCount; i++){
38515             var name = cm.getDataIndex(i);
38516             cs[i] = {
38517                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38518                 renderer : cm.getRenderer(i),
38519                 id : cm.getColumnId(i),
38520                 locked : cm.isLocked(i),
38521                 has_editor : cm.isCellEditable(i)
38522             };
38523         }
38524
38525         startRow = startRow || 0;
38526         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38527
38528         // records to render
38529         var rs = ds.getRange(startRow, endRow);
38530
38531         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38532     },
38533
38534     // As much as I hate to duplicate code, this was branched because FireFox really hates
38535     // [].join("") on strings. The performance difference was substantial enough to
38536     // branch this function
38537     doRender : Roo.isGecko ?
38538             function(cs, rs, ds, startRow, colCount, stripe){
38539                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38540                 // buffers
38541                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38542                 
38543                 var hasListener = this.grid.hasListener('rowclass');
38544                 var rowcfg = {};
38545                 for(var j = 0, len = rs.length; j < len; j++){
38546                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38547                     for(var i = 0; i < colCount; i++){
38548                         c = cs[i];
38549                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38550                         p.id = c.id;
38551                         p.css = p.attr = "";
38552                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38553                         if(p.value == undefined || p.value === "") {
38554                             p.value = "&#160;";
38555                         }
38556                         if(c.has_editor){
38557                             p.css += ' x-grid-editable-cell';
38558                         }
38559                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38560                             p.css +=  ' x-grid-dirty-cell';
38561                         }
38562                         var markup = ct.apply(p);
38563                         if(!c.locked){
38564                             cb+= markup;
38565                         }else{
38566                             lcb+= markup;
38567                         }
38568                     }
38569                     var alt = [];
38570                     if(stripe && ((rowIndex+1) % 2 == 0)){
38571                         alt.push("x-grid-row-alt")
38572                     }
38573                     if(r.dirty){
38574                         alt.push(  " x-grid-dirty-row");
38575                     }
38576                     rp.cells = lcb;
38577                     if(this.getRowClass){
38578                         alt.push(this.getRowClass(r, rowIndex));
38579                     }
38580                     if (hasListener) {
38581                         rowcfg = {
38582                              
38583                             record: r,
38584                             rowIndex : rowIndex,
38585                             rowClass : ''
38586                         };
38587                         this.grid.fireEvent('rowclass', this, rowcfg);
38588                         alt.push(rowcfg.rowClass);
38589                     }
38590                     rp.alt = alt.join(" ");
38591                     lbuf+= rt.apply(rp);
38592                     rp.cells = cb;
38593                     buf+=  rt.apply(rp);
38594                 }
38595                 return [lbuf, buf];
38596             } :
38597             function(cs, rs, ds, startRow, colCount, stripe){
38598                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38599                 // buffers
38600                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38601                 var hasListener = this.grid.hasListener('rowclass');
38602  
38603                 var rowcfg = {};
38604                 for(var j = 0, len = rs.length; j < len; j++){
38605                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38606                     for(var i = 0; i < colCount; i++){
38607                         c = cs[i];
38608                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38609                         p.id = c.id;
38610                         p.css = p.attr = "";
38611                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38612                         if(p.value == undefined || p.value === "") {
38613                             p.value = "&#160;";
38614                         }
38615                         //Roo.log(c);
38616                          if(c.has_editor){
38617                             p.css += ' x-grid-editable-cell';
38618                         }
38619                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38620                             p.css += ' x-grid-dirty-cell' 
38621                         }
38622                         
38623                         var markup = ct.apply(p);
38624                         if(!c.locked){
38625                             cb[cb.length] = markup;
38626                         }else{
38627                             lcb[lcb.length] = markup;
38628                         }
38629                     }
38630                     var alt = [];
38631                     if(stripe && ((rowIndex+1) % 2 == 0)){
38632                         alt.push( "x-grid-row-alt");
38633                     }
38634                     if(r.dirty){
38635                         alt.push(" x-grid-dirty-row");
38636                     }
38637                     rp.cells = lcb;
38638                     if(this.getRowClass){
38639                         alt.push( this.getRowClass(r, rowIndex));
38640                     }
38641                     if (hasListener) {
38642                         rowcfg = {
38643                              
38644                             record: r,
38645                             rowIndex : rowIndex,
38646                             rowClass : ''
38647                         };
38648                         this.grid.fireEvent('rowclass', this, rowcfg);
38649                         alt.push(rowcfg.rowClass);
38650                     }
38651                     
38652                     rp.alt = alt.join(" ");
38653                     rp.cells = lcb.join("");
38654                     lbuf[lbuf.length] = rt.apply(rp);
38655                     rp.cells = cb.join("");
38656                     buf[buf.length] =  rt.apply(rp);
38657                 }
38658                 return [lbuf.join(""), buf.join("")];
38659             },
38660
38661     renderBody : function(){
38662         var markup = this.renderRows();
38663         var bt = this.templates.body;
38664         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38665     },
38666
38667     /**
38668      * Refreshes the grid
38669      * @param {Boolean} headersToo
38670      */
38671     refresh : function(headersToo){
38672         this.fireEvent("beforerefresh", this);
38673         this.grid.stopEditing();
38674         var result = this.renderBody();
38675         this.lockedBody.update(result[0]);
38676         this.mainBody.update(result[1]);
38677         if(headersToo === true){
38678             this.updateHeaders();
38679             this.updateColumns();
38680             this.updateSplitters();
38681             this.updateHeaderSortState();
38682         }
38683         this.syncRowHeights();
38684         this.layout();
38685         this.fireEvent("refresh", this);
38686     },
38687
38688     handleColumnMove : function(cm, oldIndex, newIndex){
38689         this.indexMap = null;
38690         var s = this.getScrollState();
38691         this.refresh(true);
38692         this.restoreScroll(s);
38693         this.afterMove(newIndex);
38694     },
38695
38696     afterMove : function(colIndex){
38697         if(this.enableMoveAnim && Roo.enableFx){
38698             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38699         }
38700         // if multisort - fix sortOrder, and reload..
38701         if (this.grid.dataSource.multiSort) {
38702             // the we can call sort again..
38703             var dm = this.grid.dataSource;
38704             var cm = this.grid.colModel;
38705             var so = [];
38706             for(var i = 0; i < cm.config.length; i++ ) {
38707                 
38708                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38709                     continue; // dont' bother, it's not in sort list or being set.
38710                 }
38711                 
38712                 so.push(cm.config[i].dataIndex);
38713             };
38714             dm.sortOrder = so;
38715             dm.load(dm.lastOptions);
38716             
38717             
38718         }
38719         
38720     },
38721
38722     updateCell : function(dm, rowIndex, dataIndex){
38723         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38724         if(typeof colIndex == "undefined"){ // not present in grid
38725             return;
38726         }
38727         var cm = this.grid.colModel;
38728         var cell = this.getCell(rowIndex, colIndex);
38729         var cellText = this.getCellText(rowIndex, colIndex);
38730
38731         var p = {
38732             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38733             id : cm.getColumnId(colIndex),
38734             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38735         };
38736         var renderer = cm.getRenderer(colIndex);
38737         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38738         if(typeof val == "undefined" || val === "") {
38739             val = "&#160;";
38740         }
38741         cellText.innerHTML = val;
38742         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38743         this.syncRowHeights(rowIndex, rowIndex);
38744     },
38745
38746     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38747         var maxWidth = 0;
38748         if(this.grid.autoSizeHeaders){
38749             var h = this.getHeaderCellMeasure(colIndex);
38750             maxWidth = Math.max(maxWidth, h.scrollWidth);
38751         }
38752         var tb, index;
38753         if(this.cm.isLocked(colIndex)){
38754             tb = this.getLockedTable();
38755             index = colIndex;
38756         }else{
38757             tb = this.getBodyTable();
38758             index = colIndex - this.cm.getLockedCount();
38759         }
38760         if(tb && tb.rows){
38761             var rows = tb.rows;
38762             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38763             for(var i = 0; i < stopIndex; i++){
38764                 var cell = rows[i].childNodes[index].firstChild;
38765                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38766             }
38767         }
38768         return maxWidth + /*margin for error in IE*/ 5;
38769     },
38770     /**
38771      * Autofit a column to its content.
38772      * @param {Number} colIndex
38773      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38774      */
38775      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38776          if(this.cm.isHidden(colIndex)){
38777              return; // can't calc a hidden column
38778          }
38779         if(forceMinSize){
38780             var cid = this.cm.getColumnId(colIndex);
38781             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38782            if(this.grid.autoSizeHeaders){
38783                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38784            }
38785         }
38786         var newWidth = this.calcColumnWidth(colIndex);
38787         this.cm.setColumnWidth(colIndex,
38788             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38789         if(!suppressEvent){
38790             this.grid.fireEvent("columnresize", colIndex, newWidth);
38791         }
38792     },
38793
38794     /**
38795      * Autofits all columns to their content and then expands to fit any extra space in the grid
38796      */
38797      autoSizeColumns : function(){
38798         var cm = this.grid.colModel;
38799         var colCount = cm.getColumnCount();
38800         for(var i = 0; i < colCount; i++){
38801             this.autoSizeColumn(i, true, true);
38802         }
38803         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38804             this.fitColumns();
38805         }else{
38806             this.updateColumns();
38807             this.layout();
38808         }
38809     },
38810
38811     /**
38812      * Autofits all columns to the grid's width proportionate with their current size
38813      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38814      */
38815     fitColumns : function(reserveScrollSpace){
38816         var cm = this.grid.colModel;
38817         var colCount = cm.getColumnCount();
38818         var cols = [];
38819         var width = 0;
38820         var i, w;
38821         for (i = 0; i < colCount; i++){
38822             if(!cm.isHidden(i) && !cm.isFixed(i)){
38823                 w = cm.getColumnWidth(i);
38824                 cols.push(i);
38825                 cols.push(w);
38826                 width += w;
38827             }
38828         }
38829         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38830         if(reserveScrollSpace){
38831             avail -= 17;
38832         }
38833         var frac = (avail - cm.getTotalWidth())/width;
38834         while (cols.length){
38835             w = cols.pop();
38836             i = cols.pop();
38837             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38838         }
38839         this.updateColumns();
38840         this.layout();
38841     },
38842
38843     onRowSelect : function(rowIndex){
38844         var row = this.getRowComposite(rowIndex);
38845         row.addClass("x-grid-row-selected");
38846     },
38847
38848     onRowDeselect : function(rowIndex){
38849         var row = this.getRowComposite(rowIndex);
38850         row.removeClass("x-grid-row-selected");
38851     },
38852
38853     onCellSelect : function(row, col){
38854         var cell = this.getCell(row, col);
38855         if(cell){
38856             Roo.fly(cell).addClass("x-grid-cell-selected");
38857         }
38858     },
38859
38860     onCellDeselect : function(row, col){
38861         var cell = this.getCell(row, col);
38862         if(cell){
38863             Roo.fly(cell).removeClass("x-grid-cell-selected");
38864         }
38865     },
38866
38867     updateHeaderSortState : function(){
38868         
38869         // sort state can be single { field: xxx, direction : yyy}
38870         // or   { xxx=>ASC , yyy : DESC ..... }
38871         
38872         var mstate = {};
38873         if (!this.ds.multiSort) { 
38874             var state = this.ds.getSortState();
38875             if(!state){
38876                 return;
38877             }
38878             mstate[state.field] = state.direction;
38879             // FIXME... - this is not used here.. but might be elsewhere..
38880             this.sortState = state;
38881             
38882         } else {
38883             mstate = this.ds.sortToggle;
38884         }
38885         //remove existing sort classes..
38886         
38887         var sc = this.sortClasses;
38888         var hds = this.el.select(this.headerSelector).removeClass(sc);
38889         
38890         for(var f in mstate) {
38891         
38892             var sortColumn = this.cm.findColumnIndex(f);
38893             
38894             if(sortColumn != -1){
38895                 var sortDir = mstate[f];        
38896                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38897             }
38898         }
38899         
38900          
38901         
38902     },
38903
38904
38905     handleHeaderClick : function(g, index,e){
38906         
38907         Roo.log("header click");
38908         
38909         if (Roo.isTouch) {
38910             // touch events on header are handled by context
38911             this.handleHdCtx(g,index,e);
38912             return;
38913         }
38914         
38915         
38916         if(this.headersDisabled){
38917             return;
38918         }
38919         var dm = g.dataSource, cm = g.colModel;
38920         if(!cm.isSortable(index)){
38921             return;
38922         }
38923         g.stopEditing();
38924         
38925         if (dm.multiSort) {
38926             // update the sortOrder
38927             var so = [];
38928             for(var i = 0; i < cm.config.length; i++ ) {
38929                 
38930                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38931                     continue; // dont' bother, it's not in sort list or being set.
38932                 }
38933                 
38934                 so.push(cm.config[i].dataIndex);
38935             };
38936             dm.sortOrder = so;
38937         }
38938         
38939         
38940         dm.sort(cm.getDataIndex(index));
38941     },
38942
38943
38944     destroy : function(){
38945         if(this.colMenu){
38946             this.colMenu.removeAll();
38947             Roo.menu.MenuMgr.unregister(this.colMenu);
38948             this.colMenu.getEl().remove();
38949             delete this.colMenu;
38950         }
38951         if(this.hmenu){
38952             this.hmenu.removeAll();
38953             Roo.menu.MenuMgr.unregister(this.hmenu);
38954             this.hmenu.getEl().remove();
38955             delete this.hmenu;
38956         }
38957         if(this.grid.enableColumnMove){
38958             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38959             if(dds){
38960                 for(var dd in dds){
38961                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38962                         var elid = dds[dd].dragElId;
38963                         dds[dd].unreg();
38964                         Roo.get(elid).remove();
38965                     } else if(dds[dd].config.isTarget){
38966                         dds[dd].proxyTop.remove();
38967                         dds[dd].proxyBottom.remove();
38968                         dds[dd].unreg();
38969                     }
38970                     if(Roo.dd.DDM.locationCache[dd]){
38971                         delete Roo.dd.DDM.locationCache[dd];
38972                     }
38973                 }
38974                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38975             }
38976         }
38977         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38978         this.bind(null, null);
38979         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38980     },
38981
38982     handleLockChange : function(){
38983         this.refresh(true);
38984     },
38985
38986     onDenyColumnLock : function(){
38987
38988     },
38989
38990     onDenyColumnHide : function(){
38991
38992     },
38993
38994     handleHdMenuClick : function(item){
38995         var index = this.hdCtxIndex;
38996         var cm = this.cm, ds = this.ds;
38997         switch(item.id){
38998             case "asc":
38999                 ds.sort(cm.getDataIndex(index), "ASC");
39000                 break;
39001             case "desc":
39002                 ds.sort(cm.getDataIndex(index), "DESC");
39003                 break;
39004             case "lock":
39005                 var lc = cm.getLockedCount();
39006                 if(cm.getColumnCount(true) <= lc+1){
39007                     this.onDenyColumnLock();
39008                     return;
39009                 }
39010                 if(lc != index){
39011                     cm.setLocked(index, true, true);
39012                     cm.moveColumn(index, lc);
39013                     this.grid.fireEvent("columnmove", index, lc);
39014                 }else{
39015                     cm.setLocked(index, true);
39016                 }
39017             break;
39018             case "unlock":
39019                 var lc = cm.getLockedCount();
39020                 if((lc-1) != index){
39021                     cm.setLocked(index, false, true);
39022                     cm.moveColumn(index, lc-1);
39023                     this.grid.fireEvent("columnmove", index, lc-1);
39024                 }else{
39025                     cm.setLocked(index, false);
39026                 }
39027             break;
39028             case 'wider': // used to expand cols on touch..
39029             case 'narrow':
39030                 var cw = cm.getColumnWidth(index);
39031                 cw += (item.id == 'wider' ? 1 : -1) * 50;
39032                 cw = Math.max(0, cw);
39033                 cw = Math.min(cw,4000);
39034                 cm.setColumnWidth(index, cw);
39035                 break;
39036                 
39037             default:
39038                 index = cm.getIndexById(item.id.substr(4));
39039                 if(index != -1){
39040                     if(item.checked && cm.getColumnCount(true) <= 1){
39041                         this.onDenyColumnHide();
39042                         return false;
39043                     }
39044                     cm.setHidden(index, item.checked);
39045                 }
39046         }
39047         return true;
39048     },
39049
39050     beforeColMenuShow : function(){
39051         var cm = this.cm,  colCount = cm.getColumnCount();
39052         this.colMenu.removeAll();
39053         
39054         var items = [];
39055         for(var i = 0; i < colCount; i++){
39056             items.push({
39057                 id: "col-"+cm.getColumnId(i),
39058                 text: cm.getColumnHeader(i),
39059                 checked: !cm.isHidden(i),
39060                 hideOnClick:false
39061             });
39062         }
39063         
39064         if (this.grid.sortColMenu) {
39065             items.sort(function(a,b) {
39066                 if (a.text == b.text) {
39067                     return 0;
39068                 }
39069                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39070             });
39071         }
39072         
39073         for(var i = 0; i < colCount; i++){
39074             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39075         }
39076     },
39077
39078     handleHdCtx : function(g, index, e){
39079         e.stopEvent();
39080         var hd = this.getHeaderCell(index);
39081         this.hdCtxIndex = index;
39082         var ms = this.hmenu.items, cm = this.cm;
39083         ms.get("asc").setDisabled(!cm.isSortable(index));
39084         ms.get("desc").setDisabled(!cm.isSortable(index));
39085         if(this.grid.enableColLock !== false){
39086             ms.get("lock").setDisabled(cm.isLocked(index));
39087             ms.get("unlock").setDisabled(!cm.isLocked(index));
39088         }
39089         this.hmenu.show(hd, "tl-bl");
39090     },
39091
39092     handleHdOver : function(e){
39093         var hd = this.findHeaderCell(e.getTarget());
39094         if(hd && !this.headersDisabled){
39095             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39096                this.fly(hd).addClass("x-grid-hd-over");
39097             }
39098         }
39099     },
39100
39101     handleHdOut : function(e){
39102         var hd = this.findHeaderCell(e.getTarget());
39103         if(hd){
39104             this.fly(hd).removeClass("x-grid-hd-over");
39105         }
39106     },
39107
39108     handleSplitDblClick : function(e, t){
39109         var i = this.getCellIndex(t);
39110         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39111             this.autoSizeColumn(i, true);
39112             this.layout();
39113         }
39114     },
39115
39116     render : function(){
39117
39118         var cm = this.cm;
39119         var colCount = cm.getColumnCount();
39120
39121         if(this.grid.monitorWindowResize === true){
39122             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39123         }
39124         var header = this.renderHeaders();
39125         var body = this.templates.body.apply({rows:""});
39126         var html = this.templates.master.apply({
39127             lockedBody: body,
39128             body: body,
39129             lockedHeader: header[0],
39130             header: header[1]
39131         });
39132
39133         //this.updateColumns();
39134
39135         this.grid.getGridEl().dom.innerHTML = html;
39136
39137         this.initElements();
39138         
39139         // a kludge to fix the random scolling effect in webkit
39140         this.el.on("scroll", function() {
39141             this.el.dom.scrollTop=0; // hopefully not recursive..
39142         },this);
39143
39144         this.scroller.on("scroll", this.handleScroll, this);
39145         this.lockedBody.on("mousewheel", this.handleWheel, this);
39146         this.mainBody.on("mousewheel", this.handleWheel, this);
39147
39148         this.mainHd.on("mouseover", this.handleHdOver, this);
39149         this.mainHd.on("mouseout", this.handleHdOut, this);
39150         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39151                 {delegate: "."+this.splitClass});
39152
39153         this.lockedHd.on("mouseover", this.handleHdOver, this);
39154         this.lockedHd.on("mouseout", this.handleHdOut, this);
39155         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39156                 {delegate: "."+this.splitClass});
39157
39158         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39159             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39160         }
39161
39162         this.updateSplitters();
39163
39164         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39165             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39166             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39167         }
39168
39169         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39170             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39171             this.hmenu.add(
39172                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39173                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39174             );
39175             if(this.grid.enableColLock !== false){
39176                 this.hmenu.add('-',
39177                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39178                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39179                 );
39180             }
39181             if (Roo.isTouch) {
39182                  this.hmenu.add('-',
39183                     {id:"wider", text: this.columnsWiderText},
39184                     {id:"narrow", text: this.columnsNarrowText }
39185                 );
39186                 
39187                  
39188             }
39189             
39190             if(this.grid.enableColumnHide !== false){
39191
39192                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39193                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39194                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39195
39196                 this.hmenu.add('-',
39197                     {id:"columns", text: this.columnsText, menu: this.colMenu}
39198                 );
39199             }
39200             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39201
39202             this.grid.on("headercontextmenu", this.handleHdCtx, this);
39203         }
39204
39205         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39206             this.dd = new Roo.grid.GridDragZone(this.grid, {
39207                 ddGroup : this.grid.ddGroup || 'GridDD'
39208             });
39209             
39210         }
39211
39212         /*
39213         for(var i = 0; i < colCount; i++){
39214             if(cm.isHidden(i)){
39215                 this.hideColumn(i);
39216             }
39217             if(cm.config[i].align){
39218                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39219                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39220             }
39221         }*/
39222         
39223         this.updateHeaderSortState();
39224
39225         this.beforeInitialResize();
39226         this.layout(true);
39227
39228         // two part rendering gives faster view to the user
39229         this.renderPhase2.defer(1, this);
39230     },
39231
39232     renderPhase2 : function(){
39233         // render the rows now
39234         this.refresh();
39235         if(this.grid.autoSizeColumns){
39236             this.autoSizeColumns();
39237         }
39238     },
39239
39240     beforeInitialResize : function(){
39241
39242     },
39243
39244     onColumnSplitterMoved : function(i, w){
39245         this.userResized = true;
39246         var cm = this.grid.colModel;
39247         cm.setColumnWidth(i, w, true);
39248         var cid = cm.getColumnId(i);
39249         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39250         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39251         this.updateSplitters();
39252         this.layout();
39253         this.grid.fireEvent("columnresize", i, w);
39254     },
39255
39256     syncRowHeights : function(startIndex, endIndex){
39257         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39258             startIndex = startIndex || 0;
39259             var mrows = this.getBodyTable().rows;
39260             var lrows = this.getLockedTable().rows;
39261             var len = mrows.length-1;
39262             endIndex = Math.min(endIndex || len, len);
39263             for(var i = startIndex; i <= endIndex; i++){
39264                 var m = mrows[i], l = lrows[i];
39265                 var h = Math.max(m.offsetHeight, l.offsetHeight);
39266                 m.style.height = l.style.height = h + "px";
39267             }
39268         }
39269     },
39270
39271     layout : function(initialRender, is2ndPass)
39272     {
39273         var g = this.grid;
39274         var auto = g.autoHeight;
39275         var scrollOffset = 16;
39276         var c = g.getGridEl(), cm = this.cm,
39277                 expandCol = g.autoExpandColumn,
39278                 gv = this;
39279         //c.beginMeasure();
39280
39281         if(!c.dom.offsetWidth){ // display:none?
39282             if(initialRender){
39283                 this.lockedWrap.show();
39284                 this.mainWrap.show();
39285             }
39286             return;
39287         }
39288
39289         var hasLock = this.cm.isLocked(0);
39290
39291         var tbh = this.headerPanel.getHeight();
39292         var bbh = this.footerPanel.getHeight();
39293
39294         if(auto){
39295             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39296             var newHeight = ch + c.getBorderWidth("tb");
39297             if(g.maxHeight){
39298                 newHeight = Math.min(g.maxHeight, newHeight);
39299             }
39300             c.setHeight(newHeight);
39301         }
39302
39303         if(g.autoWidth){
39304             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39305         }
39306
39307         var s = this.scroller;
39308
39309         var csize = c.getSize(true);
39310
39311         this.el.setSize(csize.width, csize.height);
39312
39313         this.headerPanel.setWidth(csize.width);
39314         this.footerPanel.setWidth(csize.width);
39315
39316         var hdHeight = this.mainHd.getHeight();
39317         var vw = csize.width;
39318         var vh = csize.height - (tbh + bbh);
39319
39320         s.setSize(vw, vh);
39321
39322         var bt = this.getBodyTable();
39323         
39324         if(cm.getLockedCount() == cm.config.length){
39325             bt = this.getLockedTable();
39326         }
39327         
39328         var ltWidth = hasLock ?
39329                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39330
39331         var scrollHeight = bt.offsetHeight;
39332         var scrollWidth = ltWidth + bt.offsetWidth;
39333         var vscroll = false, hscroll = false;
39334
39335         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39336
39337         var lw = this.lockedWrap, mw = this.mainWrap;
39338         var lb = this.lockedBody, mb = this.mainBody;
39339
39340         setTimeout(function(){
39341             var t = s.dom.offsetTop;
39342             var w = s.dom.clientWidth,
39343                 h = s.dom.clientHeight;
39344
39345             lw.setTop(t);
39346             lw.setSize(ltWidth, h);
39347
39348             mw.setLeftTop(ltWidth, t);
39349             mw.setSize(w-ltWidth, h);
39350
39351             lb.setHeight(h-hdHeight);
39352             mb.setHeight(h-hdHeight);
39353
39354             if(is2ndPass !== true && !gv.userResized && expandCol){
39355                 // high speed resize without full column calculation
39356                 
39357                 var ci = cm.getIndexById(expandCol);
39358                 if (ci < 0) {
39359                     ci = cm.findColumnIndex(expandCol);
39360                 }
39361                 ci = Math.max(0, ci); // make sure it's got at least the first col.
39362                 var expandId = cm.getColumnId(ci);
39363                 var  tw = cm.getTotalWidth(false);
39364                 var currentWidth = cm.getColumnWidth(ci);
39365                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39366                 if(currentWidth != cw){
39367                     cm.setColumnWidth(ci, cw, true);
39368                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39369                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39370                     gv.updateSplitters();
39371                     gv.layout(false, true);
39372                 }
39373             }
39374
39375             if(initialRender){
39376                 lw.show();
39377                 mw.show();
39378             }
39379             //c.endMeasure();
39380         }, 10);
39381     },
39382
39383     onWindowResize : function(){
39384         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39385             return;
39386         }
39387         this.layout();
39388     },
39389
39390     appendFooter : function(parentEl){
39391         return null;
39392     },
39393
39394     sortAscText : "Sort Ascending",
39395     sortDescText : "Sort Descending",
39396     lockText : "Lock Column",
39397     unlockText : "Unlock Column",
39398     columnsText : "Columns",
39399  
39400     columnsWiderText : "Wider",
39401     columnsNarrowText : "Thinner"
39402 });
39403
39404
39405 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39406     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39407     this.proxy.el.addClass('x-grid3-col-dd');
39408 };
39409
39410 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39411     handleMouseDown : function(e){
39412
39413     },
39414
39415     callHandleMouseDown : function(e){
39416         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39417     }
39418 });
39419 /*
39420  * Based on:
39421  * Ext JS Library 1.1.1
39422  * Copyright(c) 2006-2007, Ext JS, LLC.
39423  *
39424  * Originally Released Under LGPL - original licence link has changed is not relivant.
39425  *
39426  * Fork - LGPL
39427  * <script type="text/javascript">
39428  */
39429  /**
39430  * @extends Roo.dd.DDProxy
39431  * @class Roo.grid.SplitDragZone
39432  * Support for Column Header resizing
39433  * @constructor
39434  * @param {Object} config
39435  */
39436 // private
39437 // This is a support class used internally by the Grid components
39438 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39439     this.grid = grid;
39440     this.view = grid.getView();
39441     this.proxy = this.view.resizeProxy;
39442     Roo.grid.SplitDragZone.superclass.constructor.call(
39443         this,
39444         hd, // ID
39445         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39446         {  // CONFIG
39447             dragElId : Roo.id(this.proxy.dom),
39448             resizeFrame:false
39449         }
39450     );
39451     
39452     this.setHandleElId(Roo.id(hd));
39453     if (hd2 !== false) {
39454         this.setOuterHandleElId(Roo.id(hd2));
39455     }
39456     
39457     this.scroll = false;
39458 };
39459 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39460     fly: Roo.Element.fly,
39461
39462     b4StartDrag : function(x, y){
39463         this.view.headersDisabled = true;
39464         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39465                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39466         );
39467         this.proxy.setHeight(h);
39468         
39469         // for old system colWidth really stored the actual width?
39470         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39471         // which in reality did not work.. - it worked only for fixed sizes
39472         // for resizable we need to use actual sizes.
39473         var w = this.cm.getColumnWidth(this.cellIndex);
39474         if (!this.view.mainWrap) {
39475             // bootstrap.
39476             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39477         }
39478         
39479         
39480         
39481         // this was w-this.grid.minColumnWidth;
39482         // doesnt really make sense? - w = thie curren width or the rendered one?
39483         var minw = Math.max(w-this.grid.minColumnWidth, 0);
39484         this.resetConstraints();
39485         this.setXConstraint(minw, 1000);
39486         this.setYConstraint(0, 0);
39487         this.minX = x - minw;
39488         this.maxX = x + 1000;
39489         this.startPos = x;
39490         if (!this.view.mainWrap) { // this is Bootstrap code..
39491             this.getDragEl().style.display='block';
39492         }
39493         
39494         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39495     },
39496
39497
39498     handleMouseDown : function(e){
39499         ev = Roo.EventObject.setEvent(e);
39500         var t = this.fly(ev.getTarget());
39501         if(t.hasClass("x-grid-split")){
39502             this.cellIndex = this.view.getCellIndex(t.dom);
39503             this.split = t.dom;
39504             this.cm = this.grid.colModel;
39505             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39506                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39507             }
39508         }
39509     },
39510
39511     endDrag : function(e){
39512         this.view.headersDisabled = false;
39513         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39514         var diff = endX - this.startPos;
39515         // 
39516         var w = this.cm.getColumnWidth(this.cellIndex);
39517         if (!this.view.mainWrap) {
39518             w = 0;
39519         }
39520         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39521     },
39522
39523     autoOffset : function(){
39524         this.setDelta(0,0);
39525     }
39526 });/*
39527  * Based on:
39528  * Ext JS Library 1.1.1
39529  * Copyright(c) 2006-2007, Ext JS, LLC.
39530  *
39531  * Originally Released Under LGPL - original licence link has changed is not relivant.
39532  *
39533  * Fork - LGPL
39534  * <script type="text/javascript">
39535  */
39536  
39537 // private
39538 // This is a support class used internally by the Grid components
39539 Roo.grid.GridDragZone = function(grid, config){
39540     this.view = grid.getView();
39541     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39542     if(this.view.lockedBody){
39543         this.setHandleElId(Roo.id(this.view.mainBody.dom));
39544         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39545     }
39546     this.scroll = false;
39547     this.grid = grid;
39548     this.ddel = document.createElement('div');
39549     this.ddel.className = 'x-grid-dd-wrap';
39550 };
39551
39552 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39553     ddGroup : "GridDD",
39554
39555     getDragData : function(e){
39556         var t = Roo.lib.Event.getTarget(e);
39557         var rowIndex = this.view.findRowIndex(t);
39558         var sm = this.grid.selModel;
39559             
39560         //Roo.log(rowIndex);
39561         
39562         if (sm.getSelectedCell) {
39563             // cell selection..
39564             if (!sm.getSelectedCell()) {
39565                 return false;
39566             }
39567             if (rowIndex != sm.getSelectedCell()[0]) {
39568                 return false;
39569             }
39570         
39571         }
39572         if (sm.getSelections && sm.getSelections().length < 1) {
39573             return false;
39574         }
39575         
39576         
39577         // before it used to all dragging of unseleted... - now we dont do that.
39578         if(rowIndex !== false){
39579             
39580             // if editorgrid.. 
39581             
39582             
39583             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39584                
39585             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39586               //  
39587             //}
39588             if (e.hasModifier()){
39589                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39590             }
39591             
39592             Roo.log("getDragData");
39593             
39594             return {
39595                 grid: this.grid,
39596                 ddel: this.ddel,
39597                 rowIndex: rowIndex,
39598                 selections: sm.getSelections ? sm.getSelections() : (
39599                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39600             };
39601         }
39602         return false;
39603     },
39604     
39605     
39606     onInitDrag : function(e){
39607         var data = this.dragData;
39608         this.ddel.innerHTML = this.grid.getDragDropText();
39609         this.proxy.update(this.ddel);
39610         // fire start drag?
39611     },
39612
39613     afterRepair : function(){
39614         this.dragging = false;
39615     },
39616
39617     getRepairXY : function(e, data){
39618         return false;
39619     },
39620
39621     onEndDrag : function(data, e){
39622         // fire end drag?
39623     },
39624
39625     onValidDrop : function(dd, e, id){
39626         // fire drag drop?
39627         this.hideProxy();
39628     },
39629
39630     beforeInvalidDrop : function(e, id){
39631
39632     }
39633 });/*
39634  * Based on:
39635  * Ext JS Library 1.1.1
39636  * Copyright(c) 2006-2007, Ext JS, LLC.
39637  *
39638  * Originally Released Under LGPL - original licence link has changed is not relivant.
39639  *
39640  * Fork - LGPL
39641  * <script type="text/javascript">
39642  */
39643  
39644
39645 /**
39646  * @class Roo.grid.ColumnModel
39647  * @extends Roo.util.Observable
39648  * This is the default implementation of a ColumnModel used by the Grid. It defines
39649  * the columns in the grid.
39650  * <br>Usage:<br>
39651  <pre><code>
39652  var colModel = new Roo.grid.ColumnModel([
39653         {header: "Ticker", width: 60, sortable: true, locked: true},
39654         {header: "Company Name", width: 150, sortable: true},
39655         {header: "Market Cap.", width: 100, sortable: true},
39656         {header: "$ Sales", width: 100, sortable: true, renderer: money},
39657         {header: "Employees", width: 100, sortable: true, resizable: false}
39658  ]);
39659  </code></pre>
39660  * <p>
39661  
39662  * The config options listed for this class are options which may appear in each
39663  * individual column definition.
39664  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39665  * @constructor
39666  * @param {Object} config An Array of column config objects. See this class's
39667  * config objects for details.
39668 */
39669 Roo.grid.ColumnModel = function(config){
39670         /**
39671      * The config passed into the constructor
39672      */
39673     this.config = []; //config;
39674     this.lookup = {};
39675
39676     // if no id, create one
39677     // if the column does not have a dataIndex mapping,
39678     // map it to the order it is in the config
39679     for(var i = 0, len = config.length; i < len; i++){
39680         this.addColumn(config[i]);
39681         
39682     }
39683
39684     /**
39685      * The width of columns which have no width specified (defaults to 100)
39686      * @type Number
39687      */
39688     this.defaultWidth = 100;
39689
39690     /**
39691      * Default sortable of columns which have no sortable specified (defaults to false)
39692      * @type Boolean
39693      */
39694     this.defaultSortable = false;
39695
39696     this.addEvents({
39697         /**
39698              * @event widthchange
39699              * Fires when the width of a column changes.
39700              * @param {ColumnModel} this
39701              * @param {Number} columnIndex The column index
39702              * @param {Number} newWidth The new width
39703              */
39704             "widthchange": true,
39705         /**
39706              * @event headerchange
39707              * Fires when the text of a header changes.
39708              * @param {ColumnModel} this
39709              * @param {Number} columnIndex The column index
39710              * @param {Number} newText The new header text
39711              */
39712             "headerchange": true,
39713         /**
39714              * @event hiddenchange
39715              * Fires when a column is hidden or "unhidden".
39716              * @param {ColumnModel} this
39717              * @param {Number} columnIndex The column index
39718              * @param {Boolean} hidden true if hidden, false otherwise
39719              */
39720             "hiddenchange": true,
39721             /**
39722          * @event columnmoved
39723          * Fires when a column is moved.
39724          * @param {ColumnModel} this
39725          * @param {Number} oldIndex
39726          * @param {Number} newIndex
39727          */
39728         "columnmoved" : true,
39729         /**
39730          * @event columlockchange
39731          * Fires when a column's locked state is changed
39732          * @param {ColumnModel} this
39733          * @param {Number} colIndex
39734          * @param {Boolean} locked true if locked
39735          */
39736         "columnlockchange" : true
39737     });
39738     Roo.grid.ColumnModel.superclass.constructor.call(this);
39739 };
39740 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39741     /**
39742      * @cfg {String} header The header text to display in the Grid view.
39743      */
39744         /**
39745      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
39746      */
39747         /**
39748      * @cfg {String} smHeader Header at Bootsrap Small width
39749      */
39750         /**
39751      * @cfg {String} mdHeader Header at Bootsrap Medium width
39752      */
39753         /**
39754      * @cfg {String} lgHeader Header at Bootsrap Large width
39755      */
39756         /**
39757      * @cfg {String} xlHeader Header at Bootsrap extra Large width
39758      */
39759     /**
39760      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39761      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39762      * specified, the column's index is used as an index into the Record's data Array.
39763      */
39764     /**
39765      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39766      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39767      */
39768     /**
39769      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39770      * Defaults to the value of the {@link #defaultSortable} property.
39771      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39772      */
39773     /**
39774      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39775      */
39776     /**
39777      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39778      */
39779     /**
39780      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39781      */
39782     /**
39783      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39784      */
39785     /**
39786      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39787      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39788      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
39789      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39790      */
39791        /**
39792      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39793      */
39794     /**
39795      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39796      */
39797     /**
39798      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
39799      */
39800     /**
39801      * @cfg {String} cursor (Optional)
39802      */
39803     /**
39804      * @cfg {String} tooltip (Optional)
39805      */
39806     /**
39807      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
39808      */
39809     /**
39810      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
39811      */
39812     /**
39813      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
39814      */
39815     /**
39816      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
39817      */
39818         /**
39819      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
39820      */
39821     /**
39822      * Returns the id of the column at the specified index.
39823      * @param {Number} index The column index
39824      * @return {String} the id
39825      */
39826     getColumnId : function(index){
39827         return this.config[index].id;
39828     },
39829
39830     /**
39831      * Returns the column for a specified id.
39832      * @param {String} id The column id
39833      * @return {Object} the column
39834      */
39835     getColumnById : function(id){
39836         return this.lookup[id];
39837     },
39838
39839     
39840     /**
39841      * Returns the column Object for a specified dataIndex.
39842      * @param {String} dataIndex The column dataIndex
39843      * @return {Object|Boolean} the column or false if not found
39844      */
39845     getColumnByDataIndex: function(dataIndex){
39846         var index = this.findColumnIndex(dataIndex);
39847         return index > -1 ? this.config[index] : false;
39848     },
39849     
39850     /**
39851      * Returns the index for a specified column id.
39852      * @param {String} id The column id
39853      * @return {Number} the index, or -1 if not found
39854      */
39855     getIndexById : function(id){
39856         for(var i = 0, len = this.config.length; i < len; i++){
39857             if(this.config[i].id == id){
39858                 return i;
39859             }
39860         }
39861         return -1;
39862     },
39863     
39864     /**
39865      * Returns the index for a specified column dataIndex.
39866      * @param {String} dataIndex The column dataIndex
39867      * @return {Number} the index, or -1 if not found
39868      */
39869     
39870     findColumnIndex : function(dataIndex){
39871         for(var i = 0, len = this.config.length; i < len; i++){
39872             if(this.config[i].dataIndex == dataIndex){
39873                 return i;
39874             }
39875         }
39876         return -1;
39877     },
39878     
39879     
39880     moveColumn : function(oldIndex, newIndex){
39881         var c = this.config[oldIndex];
39882         this.config.splice(oldIndex, 1);
39883         this.config.splice(newIndex, 0, c);
39884         this.dataMap = null;
39885         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39886     },
39887
39888     isLocked : function(colIndex){
39889         return this.config[colIndex].locked === true;
39890     },
39891
39892     setLocked : function(colIndex, value, suppressEvent){
39893         if(this.isLocked(colIndex) == value){
39894             return;
39895         }
39896         this.config[colIndex].locked = value;
39897         if(!suppressEvent){
39898             this.fireEvent("columnlockchange", this, colIndex, value);
39899         }
39900     },
39901
39902     getTotalLockedWidth : function(){
39903         var totalWidth = 0;
39904         for(var i = 0; i < this.config.length; i++){
39905             if(this.isLocked(i) && !this.isHidden(i)){
39906                 this.totalWidth += this.getColumnWidth(i);
39907             }
39908         }
39909         return totalWidth;
39910     },
39911
39912     getLockedCount : function(){
39913         for(var i = 0, len = this.config.length; i < len; i++){
39914             if(!this.isLocked(i)){
39915                 return i;
39916             }
39917         }
39918         
39919         return this.config.length;
39920     },
39921
39922     /**
39923      * Returns the number of columns.
39924      * @return {Number}
39925      */
39926     getColumnCount : function(visibleOnly){
39927         if(visibleOnly === true){
39928             var c = 0;
39929             for(var i = 0, len = this.config.length; i < len; i++){
39930                 if(!this.isHidden(i)){
39931                     c++;
39932                 }
39933             }
39934             return c;
39935         }
39936         return this.config.length;
39937     },
39938
39939     /**
39940      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39941      * @param {Function} fn
39942      * @param {Object} scope (optional)
39943      * @return {Array} result
39944      */
39945     getColumnsBy : function(fn, scope){
39946         var r = [];
39947         for(var i = 0, len = this.config.length; i < len; i++){
39948             var c = this.config[i];
39949             if(fn.call(scope||this, c, i) === true){
39950                 r[r.length] = c;
39951             }
39952         }
39953         return r;
39954     },
39955
39956     /**
39957      * Returns true if the specified column is sortable.
39958      * @param {Number} col The column index
39959      * @return {Boolean}
39960      */
39961     isSortable : function(col){
39962         if(typeof this.config[col].sortable == "undefined"){
39963             return this.defaultSortable;
39964         }
39965         return this.config[col].sortable;
39966     },
39967
39968     /**
39969      * Returns the rendering (formatting) function defined for the column.
39970      * @param {Number} col The column index.
39971      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39972      */
39973     getRenderer : function(col){
39974         if(!this.config[col].renderer){
39975             return Roo.grid.ColumnModel.defaultRenderer;
39976         }
39977         return this.config[col].renderer;
39978     },
39979
39980     /**
39981      * Sets the rendering (formatting) function for a column.
39982      * @param {Number} col The column index
39983      * @param {Function} fn The function to use to process the cell's raw data
39984      * to return HTML markup for the grid view. The render function is called with
39985      * the following parameters:<ul>
39986      * <li>Data value.</li>
39987      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39988      * <li>css A CSS style string to apply to the table cell.</li>
39989      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39990      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39991      * <li>Row index</li>
39992      * <li>Column index</li>
39993      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39994      */
39995     setRenderer : function(col, fn){
39996         this.config[col].renderer = fn;
39997     },
39998
39999     /**
40000      * Returns the width for the specified column.
40001      * @param {Number} col The column index
40002      * @param (optional) {String} gridSize bootstrap width size.
40003      * @return {Number}
40004      */
40005     getColumnWidth : function(col, gridSize)
40006         {
40007                 var cfg = this.config[col];
40008                 
40009                 if (typeof(gridSize) == 'undefined') {
40010                         return cfg.width * 1 || this.defaultWidth;
40011                 }
40012                 if (gridSize === false) { // if we set it..
40013                         return cfg.width || false;
40014                 }
40015                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40016                 
40017                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40018                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40019                                 continue;
40020                         }
40021                         return cfg[ sizes[i] ];
40022                 }
40023                 return 1;
40024                 
40025     },
40026
40027     /**
40028      * Sets the width for a column.
40029      * @param {Number} col The column index
40030      * @param {Number} width The new width
40031      */
40032     setColumnWidth : function(col, width, suppressEvent){
40033         this.config[col].width = width;
40034         this.totalWidth = null;
40035         if(!suppressEvent){
40036              this.fireEvent("widthchange", this, col, width);
40037         }
40038     },
40039
40040     /**
40041      * Returns the total width of all columns.
40042      * @param {Boolean} includeHidden True to include hidden column widths
40043      * @return {Number}
40044      */
40045     getTotalWidth : function(includeHidden){
40046         if(!this.totalWidth){
40047             this.totalWidth = 0;
40048             for(var i = 0, len = this.config.length; i < len; i++){
40049                 if(includeHidden || !this.isHidden(i)){
40050                     this.totalWidth += this.getColumnWidth(i);
40051                 }
40052             }
40053         }
40054         return this.totalWidth;
40055     },
40056
40057     /**
40058      * Returns the header for the specified column.
40059      * @param {Number} col The column index
40060      * @return {String}
40061      */
40062     getColumnHeader : function(col){
40063         return this.config[col].header;
40064     },
40065
40066     /**
40067      * Sets the header for a column.
40068      * @param {Number} col The column index
40069      * @param {String} header The new header
40070      */
40071     setColumnHeader : function(col, header){
40072         this.config[col].header = header;
40073         this.fireEvent("headerchange", this, col, header);
40074     },
40075
40076     /**
40077      * Returns the tooltip for the specified column.
40078      * @param {Number} col The column index
40079      * @return {String}
40080      */
40081     getColumnTooltip : function(col){
40082             return this.config[col].tooltip;
40083     },
40084     /**
40085      * Sets the tooltip for a column.
40086      * @param {Number} col The column index
40087      * @param {String} tooltip The new tooltip
40088      */
40089     setColumnTooltip : function(col, tooltip){
40090             this.config[col].tooltip = tooltip;
40091     },
40092
40093     /**
40094      * Returns the dataIndex for the specified column.
40095      * @param {Number} col The column index
40096      * @return {Number}
40097      */
40098     getDataIndex : function(col){
40099         return this.config[col].dataIndex;
40100     },
40101
40102     /**
40103      * Sets the dataIndex for a column.
40104      * @param {Number} col The column index
40105      * @param {Number} dataIndex The new dataIndex
40106      */
40107     setDataIndex : function(col, dataIndex){
40108         this.config[col].dataIndex = dataIndex;
40109     },
40110
40111     
40112     
40113     /**
40114      * Returns true if the cell is editable.
40115      * @param {Number} colIndex The column index
40116      * @param {Number} rowIndex The row index - this is nto actually used..?
40117      * @return {Boolean}
40118      */
40119     isCellEditable : function(colIndex, rowIndex){
40120         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40121     },
40122
40123     /**
40124      * Returns the editor defined for the cell/column.
40125      * return false or null to disable editing.
40126      * @param {Number} colIndex The column index
40127      * @param {Number} rowIndex The row index
40128      * @return {Object}
40129      */
40130     getCellEditor : function(colIndex, rowIndex){
40131         return this.config[colIndex].editor;
40132     },
40133
40134     /**
40135      * Sets if a column is editable.
40136      * @param {Number} col The column index
40137      * @param {Boolean} editable True if the column is editable
40138      */
40139     setEditable : function(col, editable){
40140         this.config[col].editable = editable;
40141     },
40142
40143
40144     /**
40145      * Returns true if the column is hidden.
40146      * @param {Number} colIndex The column index
40147      * @return {Boolean}
40148      */
40149     isHidden : function(colIndex){
40150         return this.config[colIndex].hidden;
40151     },
40152
40153
40154     /**
40155      * Returns true if the column width cannot be changed
40156      */
40157     isFixed : function(colIndex){
40158         return this.config[colIndex].fixed;
40159     },
40160
40161     /**
40162      * Returns true if the column can be resized
40163      * @return {Boolean}
40164      */
40165     isResizable : function(colIndex){
40166         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40167     },
40168     /**
40169      * Sets if a column is hidden.
40170      * @param {Number} colIndex The column index
40171      * @param {Boolean} hidden True if the column is hidden
40172      */
40173     setHidden : function(colIndex, hidden){
40174         this.config[colIndex].hidden = hidden;
40175         this.totalWidth = null;
40176         this.fireEvent("hiddenchange", this, colIndex, hidden);
40177     },
40178
40179     /**
40180      * Sets the editor for a column.
40181      * @param {Number} col The column index
40182      * @param {Object} editor The editor object
40183      */
40184     setEditor : function(col, editor){
40185         this.config[col].editor = editor;
40186     },
40187     /**
40188      * Add a column (experimental...) - defaults to adding to the end..
40189      * @param {Object} config 
40190     */
40191     addColumn : function(c)
40192     {
40193     
40194         var i = this.config.length;
40195         this.config[i] = c;
40196         
40197         if(typeof c.dataIndex == "undefined"){
40198             c.dataIndex = i;
40199         }
40200         if(typeof c.renderer == "string"){
40201             c.renderer = Roo.util.Format[c.renderer];
40202         }
40203         if(typeof c.id == "undefined"){
40204             c.id = Roo.id();
40205         }
40206         if(c.editor && c.editor.xtype){
40207             c.editor  = Roo.factory(c.editor, Roo.grid);
40208         }
40209         if(c.editor && c.editor.isFormField){
40210             c.editor = new Roo.grid.GridEditor(c.editor);
40211         }
40212         this.lookup[c.id] = c;
40213     }
40214     
40215 });
40216
40217 Roo.grid.ColumnModel.defaultRenderer = function(value)
40218 {
40219     if(typeof value == "object") {
40220         return value;
40221     }
40222         if(typeof value == "string" && value.length < 1){
40223             return "&#160;";
40224         }
40225     
40226         return String.format("{0}", value);
40227 };
40228
40229 // Alias for backwards compatibility
40230 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40231 /*
40232  * Based on:
40233  * Ext JS Library 1.1.1
40234  * Copyright(c) 2006-2007, Ext JS, LLC.
40235  *
40236  * Originally Released Under LGPL - original licence link has changed is not relivant.
40237  *
40238  * Fork - LGPL
40239  * <script type="text/javascript">
40240  */
40241
40242 /**
40243  * @class Roo.grid.AbstractSelectionModel
40244  * @extends Roo.util.Observable
40245  * @abstract
40246  * Abstract base class for grid SelectionModels.  It provides the interface that should be
40247  * implemented by descendant classes.  This class should not be directly instantiated.
40248  * @constructor
40249  */
40250 Roo.grid.AbstractSelectionModel = function(){
40251     this.locked = false;
40252     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40253 };
40254
40255 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
40256     /** @ignore Called by the grid automatically. Do not call directly. */
40257     init : function(grid){
40258         this.grid = grid;
40259         this.initEvents();
40260     },
40261
40262     /**
40263      * Locks the selections.
40264      */
40265     lock : function(){
40266         this.locked = true;
40267     },
40268
40269     /**
40270      * Unlocks the selections.
40271      */
40272     unlock : function(){
40273         this.locked = false;
40274     },
40275
40276     /**
40277      * Returns true if the selections are locked.
40278      * @return {Boolean}
40279      */
40280     isLocked : function(){
40281         return this.locked;
40282     }
40283 });/*
40284  * Based on:
40285  * Ext JS Library 1.1.1
40286  * Copyright(c) 2006-2007, Ext JS, LLC.
40287  *
40288  * Originally Released Under LGPL - original licence link has changed is not relivant.
40289  *
40290  * Fork - LGPL
40291  * <script type="text/javascript">
40292  */
40293 /**
40294  * @extends Roo.grid.AbstractSelectionModel
40295  * @class Roo.grid.RowSelectionModel
40296  * The default SelectionModel used by {@link Roo.grid.Grid}.
40297  * It supports multiple selections and keyboard selection/navigation. 
40298  * @constructor
40299  * @param {Object} config
40300  */
40301 Roo.grid.RowSelectionModel = function(config){
40302     Roo.apply(this, config);
40303     this.selections = new Roo.util.MixedCollection(false, function(o){
40304         return o.id;
40305     });
40306
40307     this.last = false;
40308     this.lastActive = false;
40309
40310     this.addEvents({
40311         /**
40312         * @event selectionchange
40313         * Fires when the selection changes
40314         * @param {SelectionModel} this
40315         */
40316        "selectionchange" : true,
40317        /**
40318         * @event afterselectionchange
40319         * Fires after the selection changes (eg. by key press or clicking)
40320         * @param {SelectionModel} this
40321         */
40322        "afterselectionchange" : true,
40323        /**
40324         * @event beforerowselect
40325         * Fires when a row is selected being selected, return false to cancel.
40326         * @param {SelectionModel} this
40327         * @param {Number} rowIndex The selected index
40328         * @param {Boolean} keepExisting False if other selections will be cleared
40329         */
40330        "beforerowselect" : true,
40331        /**
40332         * @event rowselect
40333         * Fires when a row is selected.
40334         * @param {SelectionModel} this
40335         * @param {Number} rowIndex The selected index
40336         * @param {Roo.data.Record} r The record
40337         */
40338        "rowselect" : true,
40339        /**
40340         * @event rowdeselect
40341         * Fires when a row is deselected.
40342         * @param {SelectionModel} this
40343         * @param {Number} rowIndex The selected index
40344         */
40345         "rowdeselect" : true
40346     });
40347     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40348     this.locked = false;
40349 };
40350
40351 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
40352     /**
40353      * @cfg {Boolean} singleSelect
40354      * True to allow selection of only one row at a time (defaults to false)
40355      */
40356     singleSelect : false,
40357
40358     // private
40359     initEvents : function(){
40360
40361         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40362             this.grid.on("mousedown", this.handleMouseDown, this);
40363         }else{ // allow click to work like normal
40364             this.grid.on("rowclick", this.handleDragableRowClick, this);
40365         }
40366         // bootstrap does not have a view..
40367         var view = this.grid.view ? this.grid.view : this.grid;
40368         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40369             "up" : function(e){
40370                 if(!e.shiftKey){
40371                     this.selectPrevious(e.shiftKey);
40372                 }else if(this.last !== false && this.lastActive !== false){
40373                     var last = this.last;
40374                     this.selectRange(this.last,  this.lastActive-1);
40375                     view.focusRow(this.lastActive);
40376                     if(last !== false){
40377                         this.last = last;
40378                     }
40379                 }else{
40380                     this.selectFirstRow();
40381                 }
40382                 this.fireEvent("afterselectionchange", this);
40383             },
40384             "down" : function(e){
40385                 if(!e.shiftKey){
40386                     this.selectNext(e.shiftKey);
40387                 }else if(this.last !== false && this.lastActive !== false){
40388                     var last = this.last;
40389                     this.selectRange(this.last,  this.lastActive+1);
40390                     view.focusRow(this.lastActive);
40391                     if(last !== false){
40392                         this.last = last;
40393                     }
40394                 }else{
40395                     this.selectFirstRow();
40396                 }
40397                 this.fireEvent("afterselectionchange", this);
40398             },
40399             scope: this
40400         });
40401
40402          
40403         view.on("refresh", this.onRefresh, this);
40404         view.on("rowupdated", this.onRowUpdated, this);
40405         view.on("rowremoved", this.onRemove, this);
40406     },
40407
40408     // private
40409     onRefresh : function(){
40410         var ds = this.grid.ds, i, v = this.grid.view;
40411         var s = this.selections;
40412         s.each(function(r){
40413             if((i = ds.indexOfId(r.id)) != -1){
40414                 v.onRowSelect(i);
40415                 s.add(ds.getAt(i)); // updating the selection relate data
40416             }else{
40417                 s.remove(r);
40418             }
40419         });
40420     },
40421
40422     // private
40423     onRemove : function(v, index, r){
40424         this.selections.remove(r);
40425     },
40426
40427     // private
40428     onRowUpdated : function(v, index, r){
40429         if(this.isSelected(r)){
40430             v.onRowSelect(index);
40431         }
40432     },
40433
40434     /**
40435      * Select records.
40436      * @param {Array} records The records to select
40437      * @param {Boolean} keepExisting (optional) True to keep existing selections
40438      */
40439     selectRecords : function(records, keepExisting){
40440         if(!keepExisting){
40441             this.clearSelections();
40442         }
40443         var ds = this.grid.ds;
40444         for(var i = 0, len = records.length; i < len; i++){
40445             this.selectRow(ds.indexOf(records[i]), true);
40446         }
40447     },
40448
40449     /**
40450      * Gets the number of selected rows.
40451      * @return {Number}
40452      */
40453     getCount : function(){
40454         return this.selections.length;
40455     },
40456
40457     /**
40458      * Selects the first row in the grid.
40459      */
40460     selectFirstRow : function(){
40461         this.selectRow(0);
40462     },
40463
40464     /**
40465      * Select the last row.
40466      * @param {Boolean} keepExisting (optional) True to keep existing selections
40467      */
40468     selectLastRow : function(keepExisting){
40469         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40470     },
40471
40472     /**
40473      * Selects the row immediately following the last selected row.
40474      * @param {Boolean} keepExisting (optional) True to keep existing selections
40475      */
40476     selectNext : function(keepExisting){
40477         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40478             this.selectRow(this.last+1, keepExisting);
40479             var view = this.grid.view ? this.grid.view : this.grid;
40480             view.focusRow(this.last);
40481         }
40482     },
40483
40484     /**
40485      * Selects the row that precedes the last selected row.
40486      * @param {Boolean} keepExisting (optional) True to keep existing selections
40487      */
40488     selectPrevious : function(keepExisting){
40489         if(this.last){
40490             this.selectRow(this.last-1, keepExisting);
40491             var view = this.grid.view ? this.grid.view : this.grid;
40492             view.focusRow(this.last);
40493         }
40494     },
40495
40496     /**
40497      * Returns the selected records
40498      * @return {Array} Array of selected records
40499      */
40500     getSelections : function(){
40501         return [].concat(this.selections.items);
40502     },
40503
40504     /**
40505      * Returns the first selected record.
40506      * @return {Record}
40507      */
40508     getSelected : function(){
40509         return this.selections.itemAt(0);
40510     },
40511
40512
40513     /**
40514      * Clears all selections.
40515      */
40516     clearSelections : function(fast){
40517         if(this.locked) {
40518             return;
40519         }
40520         if(fast !== true){
40521             var ds = this.grid.ds;
40522             var s = this.selections;
40523             s.each(function(r){
40524                 this.deselectRow(ds.indexOfId(r.id));
40525             }, this);
40526             s.clear();
40527         }else{
40528             this.selections.clear();
40529         }
40530         this.last = false;
40531     },
40532
40533
40534     /**
40535      * Selects all rows.
40536      */
40537     selectAll : function(){
40538         if(this.locked) {
40539             return;
40540         }
40541         this.selections.clear();
40542         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40543             this.selectRow(i, true);
40544         }
40545     },
40546
40547     /**
40548      * Returns True if there is a selection.
40549      * @return {Boolean}
40550      */
40551     hasSelection : function(){
40552         return this.selections.length > 0;
40553     },
40554
40555     /**
40556      * Returns True if the specified row is selected.
40557      * @param {Number/Record} record The record or index of the record to check
40558      * @return {Boolean}
40559      */
40560     isSelected : function(index){
40561         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40562         return (r && this.selections.key(r.id) ? true : false);
40563     },
40564
40565     /**
40566      * Returns True if the specified record id is selected.
40567      * @param {String} id The id of record to check
40568      * @return {Boolean}
40569      */
40570     isIdSelected : function(id){
40571         return (this.selections.key(id) ? true : false);
40572     },
40573
40574     // private
40575     handleMouseDown : function(e, t)
40576     {
40577         var view = this.grid.view ? this.grid.view : this.grid;
40578         var rowIndex;
40579         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40580             return;
40581         };
40582         if(e.shiftKey && this.last !== false){
40583             var last = this.last;
40584             this.selectRange(last, rowIndex, e.ctrlKey);
40585             this.last = last; // reset the last
40586             view.focusRow(rowIndex);
40587         }else{
40588             var isSelected = this.isSelected(rowIndex);
40589             if(e.button !== 0 && isSelected){
40590                 view.focusRow(rowIndex);
40591             }else if(e.ctrlKey && isSelected){
40592                 this.deselectRow(rowIndex);
40593             }else if(!isSelected){
40594                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40595                 view.focusRow(rowIndex);
40596             }
40597         }
40598         this.fireEvent("afterselectionchange", this);
40599     },
40600     // private
40601     handleDragableRowClick :  function(grid, rowIndex, e) 
40602     {
40603         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40604             this.selectRow(rowIndex, false);
40605             var view = this.grid.view ? this.grid.view : this.grid;
40606             view.focusRow(rowIndex);
40607              this.fireEvent("afterselectionchange", this);
40608         }
40609     },
40610     
40611     /**
40612      * Selects multiple rows.
40613      * @param {Array} rows Array of the indexes of the row to select
40614      * @param {Boolean} keepExisting (optional) True to keep existing selections
40615      */
40616     selectRows : function(rows, keepExisting){
40617         if(!keepExisting){
40618             this.clearSelections();
40619         }
40620         for(var i = 0, len = rows.length; i < len; i++){
40621             this.selectRow(rows[i], true);
40622         }
40623     },
40624
40625     /**
40626      * Selects a range of rows. All rows in between startRow and endRow are also selected.
40627      * @param {Number} startRow The index of the first row in the range
40628      * @param {Number} endRow The index of the last row in the range
40629      * @param {Boolean} keepExisting (optional) True to retain existing selections
40630      */
40631     selectRange : function(startRow, endRow, keepExisting){
40632         if(this.locked) {
40633             return;
40634         }
40635         if(!keepExisting){
40636             this.clearSelections();
40637         }
40638         if(startRow <= endRow){
40639             for(var i = startRow; i <= endRow; i++){
40640                 this.selectRow(i, true);
40641             }
40642         }else{
40643             for(var i = startRow; i >= endRow; i--){
40644                 this.selectRow(i, true);
40645             }
40646         }
40647     },
40648
40649     /**
40650      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40651      * @param {Number} startRow The index of the first row in the range
40652      * @param {Number} endRow The index of the last row in the range
40653      */
40654     deselectRange : function(startRow, endRow, preventViewNotify){
40655         if(this.locked) {
40656             return;
40657         }
40658         for(var i = startRow; i <= endRow; i++){
40659             this.deselectRow(i, preventViewNotify);
40660         }
40661     },
40662
40663     /**
40664      * Selects a row.
40665      * @param {Number} row The index of the row to select
40666      * @param {Boolean} keepExisting (optional) True to keep existing selections
40667      */
40668     selectRow : function(index, keepExisting, preventViewNotify){
40669         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40670             return;
40671         }
40672         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40673             if(!keepExisting || this.singleSelect){
40674                 this.clearSelections();
40675             }
40676             var r = this.grid.ds.getAt(index);
40677             this.selections.add(r);
40678             this.last = this.lastActive = index;
40679             if(!preventViewNotify){
40680                 var view = this.grid.view ? this.grid.view : this.grid;
40681                 view.onRowSelect(index);
40682             }
40683             this.fireEvent("rowselect", this, index, r);
40684             this.fireEvent("selectionchange", this);
40685         }
40686     },
40687
40688     /**
40689      * Deselects a row.
40690      * @param {Number} row The index of the row to deselect
40691      */
40692     deselectRow : function(index, preventViewNotify){
40693         if(this.locked) {
40694             return;
40695         }
40696         if(this.last == index){
40697             this.last = false;
40698         }
40699         if(this.lastActive == index){
40700             this.lastActive = false;
40701         }
40702         var r = this.grid.ds.getAt(index);
40703         this.selections.remove(r);
40704         if(!preventViewNotify){
40705             var view = this.grid.view ? this.grid.view : this.grid;
40706             view.onRowDeselect(index);
40707         }
40708         this.fireEvent("rowdeselect", this, index);
40709         this.fireEvent("selectionchange", this);
40710     },
40711
40712     // private
40713     restoreLast : function(){
40714         if(this._last){
40715             this.last = this._last;
40716         }
40717     },
40718
40719     // private
40720     acceptsNav : function(row, col, cm){
40721         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40722     },
40723
40724     // private
40725     onEditorKey : function(field, e){
40726         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
40727         if(k == e.TAB){
40728             e.stopEvent();
40729             ed.completeEdit();
40730             if(e.shiftKey){
40731                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40732             }else{
40733                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40734             }
40735         }else if(k == e.ENTER && !e.ctrlKey){
40736             e.stopEvent();
40737             ed.completeEdit();
40738             if(e.shiftKey){
40739                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40740             }else{
40741                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40742             }
40743         }else if(k == e.ESC){
40744             ed.cancelEdit();
40745         }
40746         if(newCell){
40747             g.startEditing(newCell[0], newCell[1]);
40748         }
40749     }
40750 });/*
40751  * Based on:
40752  * Ext JS Library 1.1.1
40753  * Copyright(c) 2006-2007, Ext JS, LLC.
40754  *
40755  * Originally Released Under LGPL - original licence link has changed is not relivant.
40756  *
40757  * Fork - LGPL
40758  * <script type="text/javascript">
40759  */
40760 /**
40761  * @class Roo.grid.CellSelectionModel
40762  * @extends Roo.grid.AbstractSelectionModel
40763  * This class provides the basic implementation for cell selection in a grid.
40764  * @constructor
40765  * @param {Object} config The object containing the configuration of this model.
40766  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40767  */
40768 Roo.grid.CellSelectionModel = function(config){
40769     Roo.apply(this, config);
40770
40771     this.selection = null;
40772
40773     this.addEvents({
40774         /**
40775              * @event beforerowselect
40776              * Fires before a cell is selected.
40777              * @param {SelectionModel} this
40778              * @param {Number} rowIndex The selected row index
40779              * @param {Number} colIndex The selected cell index
40780              */
40781             "beforecellselect" : true,
40782         /**
40783              * @event cellselect
40784              * Fires when a cell is selected.
40785              * @param {SelectionModel} this
40786              * @param {Number} rowIndex The selected row index
40787              * @param {Number} colIndex The selected cell index
40788              */
40789             "cellselect" : true,
40790         /**
40791              * @event selectionchange
40792              * Fires when the active selection changes.
40793              * @param {SelectionModel} this
40794              * @param {Object} selection null for no selection or an object (o) with two properties
40795                 <ul>
40796                 <li>o.record: the record object for the row the selection is in</li>
40797                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40798                 </ul>
40799              */
40800             "selectionchange" : true,
40801         /**
40802              * @event tabend
40803              * Fires when the tab (or enter) was pressed on the last editable cell
40804              * You can use this to trigger add new row.
40805              * @param {SelectionModel} this
40806              */
40807             "tabend" : true,
40808          /**
40809              * @event beforeeditnext
40810              * Fires before the next editable sell is made active
40811              * You can use this to skip to another cell or fire the tabend
40812              *    if you set cell to false
40813              * @param {Object} eventdata object : { cell : [ row, col ] } 
40814              */
40815             "beforeeditnext" : true
40816     });
40817     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40818 };
40819
40820 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40821     
40822     enter_is_tab: false,
40823
40824     /** @ignore */
40825     initEvents : function(){
40826         this.grid.on("mousedown", this.handleMouseDown, this);
40827         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40828         var view = this.grid.view;
40829         view.on("refresh", this.onViewChange, this);
40830         view.on("rowupdated", this.onRowUpdated, this);
40831         view.on("beforerowremoved", this.clearSelections, this);
40832         view.on("beforerowsinserted", this.clearSelections, this);
40833         if(this.grid.isEditor){
40834             this.grid.on("beforeedit", this.beforeEdit,  this);
40835         }
40836     },
40837
40838         //private
40839     beforeEdit : function(e){
40840         this.select(e.row, e.column, false, true, e.record);
40841     },
40842
40843         //private
40844     onRowUpdated : function(v, index, r){
40845         if(this.selection && this.selection.record == r){
40846             v.onCellSelect(index, this.selection.cell[1]);
40847         }
40848     },
40849
40850         //private
40851     onViewChange : function(){
40852         this.clearSelections(true);
40853     },
40854
40855         /**
40856          * Returns the currently selected cell,.
40857          * @return {Array} The selected cell (row, column) or null if none selected.
40858          */
40859     getSelectedCell : function(){
40860         return this.selection ? this.selection.cell : null;
40861     },
40862
40863     /**
40864      * Clears all selections.
40865      * @param {Boolean} true to prevent the gridview from being notified about the change.
40866      */
40867     clearSelections : function(preventNotify){
40868         var s = this.selection;
40869         if(s){
40870             if(preventNotify !== true){
40871                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40872             }
40873             this.selection = null;
40874             this.fireEvent("selectionchange", this, null);
40875         }
40876     },
40877
40878     /**
40879      * Returns true if there is a selection.
40880      * @return {Boolean}
40881      */
40882     hasSelection : function(){
40883         return this.selection ? true : false;
40884     },
40885
40886     /** @ignore */
40887     handleMouseDown : function(e, t){
40888         var v = this.grid.getView();
40889         if(this.isLocked()){
40890             return;
40891         };
40892         var row = v.findRowIndex(t);
40893         var cell = v.findCellIndex(t);
40894         if(row !== false && cell !== false){
40895             this.select(row, cell);
40896         }
40897     },
40898
40899     /**
40900      * Selects a cell.
40901      * @param {Number} rowIndex
40902      * @param {Number} collIndex
40903      */
40904     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40905         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40906             this.clearSelections();
40907             r = r || this.grid.dataSource.getAt(rowIndex);
40908             this.selection = {
40909                 record : r,
40910                 cell : [rowIndex, colIndex]
40911             };
40912             if(!preventViewNotify){
40913                 var v = this.grid.getView();
40914                 v.onCellSelect(rowIndex, colIndex);
40915                 if(preventFocus !== true){
40916                     v.focusCell(rowIndex, colIndex);
40917                 }
40918             }
40919             this.fireEvent("cellselect", this, rowIndex, colIndex);
40920             this.fireEvent("selectionchange", this, this.selection);
40921         }
40922     },
40923
40924         //private
40925     isSelectable : function(rowIndex, colIndex, cm){
40926         return !cm.isHidden(colIndex);
40927     },
40928
40929     /** @ignore */
40930     handleKeyDown : function(e){
40931         //Roo.log('Cell Sel Model handleKeyDown');
40932         if(!e.isNavKeyPress()){
40933             return;
40934         }
40935         var g = this.grid, s = this.selection;
40936         if(!s){
40937             e.stopEvent();
40938             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40939             if(cell){
40940                 this.select(cell[0], cell[1]);
40941             }
40942             return;
40943         }
40944         var sm = this;
40945         var walk = function(row, col, step){
40946             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40947         };
40948         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40949         var newCell;
40950
40951       
40952
40953         switch(k){
40954             case e.TAB:
40955                 // handled by onEditorKey
40956                 if (g.isEditor && g.editing) {
40957                     return;
40958                 }
40959                 if(e.shiftKey) {
40960                     newCell = walk(r, c-1, -1);
40961                 } else {
40962                     newCell = walk(r, c+1, 1);
40963                 }
40964                 break;
40965             
40966             case e.DOWN:
40967                newCell = walk(r+1, c, 1);
40968                 break;
40969             
40970             case e.UP:
40971                 newCell = walk(r-1, c, -1);
40972                 break;
40973             
40974             case e.RIGHT:
40975                 newCell = walk(r, c+1, 1);
40976                 break;
40977             
40978             case e.LEFT:
40979                 newCell = walk(r, c-1, -1);
40980                 break;
40981             
40982             case e.ENTER:
40983                 
40984                 if(g.isEditor && !g.editing){
40985                    g.startEditing(r, c);
40986                    e.stopEvent();
40987                    return;
40988                 }
40989                 
40990                 
40991              break;
40992         };
40993         if(newCell){
40994             this.select(newCell[0], newCell[1]);
40995             e.stopEvent();
40996             
40997         }
40998     },
40999
41000     acceptsNav : function(row, col, cm){
41001         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41002     },
41003     /**
41004      * Selects a cell.
41005      * @param {Number} field (not used) - as it's normally used as a listener
41006      * @param {Number} e - event - fake it by using
41007      *
41008      * var e = Roo.EventObjectImpl.prototype;
41009      * e.keyCode = e.TAB
41010      *
41011      * 
41012      */
41013     onEditorKey : function(field, e){
41014         
41015         var k = e.getKey(),
41016             newCell,
41017             g = this.grid,
41018             ed = g.activeEditor,
41019             forward = false;
41020         ///Roo.log('onEditorKey' + k);
41021         
41022         
41023         if (this.enter_is_tab && k == e.ENTER) {
41024             k = e.TAB;
41025         }
41026         
41027         if(k == e.TAB){
41028             if(e.shiftKey){
41029                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41030             }else{
41031                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41032                 forward = true;
41033             }
41034             
41035             e.stopEvent();
41036             
41037         } else if(k == e.ENTER &&  !e.ctrlKey){
41038             ed.completeEdit();
41039             e.stopEvent();
41040             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41041         
41042                 } else if(k == e.ESC){
41043             ed.cancelEdit();
41044         }
41045                 
41046         if (newCell) {
41047             var ecall = { cell : newCell, forward : forward };
41048             this.fireEvent('beforeeditnext', ecall );
41049             newCell = ecall.cell;
41050                         forward = ecall.forward;
41051         }
41052                 
41053         if(newCell){
41054             //Roo.log('next cell after edit');
41055             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41056         } else if (forward) {
41057             // tabbed past last
41058             this.fireEvent.defer(100, this, ['tabend',this]);
41059         }
41060     }
41061 });/*
41062  * Based on:
41063  * Ext JS Library 1.1.1
41064  * Copyright(c) 2006-2007, Ext JS, LLC.
41065  *
41066  * Originally Released Under LGPL - original licence link has changed is not relivant.
41067  *
41068  * Fork - LGPL
41069  * <script type="text/javascript">
41070  */
41071  
41072 /**
41073  * @class Roo.grid.EditorGrid
41074  * @extends Roo.grid.Grid
41075  * Class for creating and editable grid.
41076  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
41077  * The container MUST have some type of size defined for the grid to fill. The container will be 
41078  * automatically set to position relative if it isn't already.
41079  * @param {Object} dataSource The data model to bind to
41080  * @param {Object} colModel The column model with info about this grid's columns
41081  */
41082 Roo.grid.EditorGrid = function(container, config){
41083     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41084     this.getGridEl().addClass("xedit-grid");
41085
41086     if(!this.selModel){
41087         this.selModel = new Roo.grid.CellSelectionModel();
41088     }
41089
41090     this.activeEditor = null;
41091
41092         this.addEvents({
41093             /**
41094              * @event beforeedit
41095              * Fires before cell editing is triggered. The edit event object has the following properties <br />
41096              * <ul style="padding:5px;padding-left:16px;">
41097              * <li>grid - This grid</li>
41098              * <li>record - The record being edited</li>
41099              * <li>field - The field name being edited</li>
41100              * <li>value - The value for the field being edited.</li>
41101              * <li>row - The grid row index</li>
41102              * <li>column - The grid column index</li>
41103              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41104              * </ul>
41105              * @param {Object} e An edit event (see above for description)
41106              */
41107             "beforeedit" : true,
41108             /**
41109              * @event afteredit
41110              * Fires after a cell is edited. <br />
41111              * <ul style="padding:5px;padding-left:16px;">
41112              * <li>grid - This grid</li>
41113              * <li>record - The record being edited</li>
41114              * <li>field - The field name being edited</li>
41115              * <li>value - The value being set</li>
41116              * <li>originalValue - The original value for the field, before the edit.</li>
41117              * <li>row - The grid row index</li>
41118              * <li>column - The grid column index</li>
41119              * </ul>
41120              * @param {Object} e An edit event (see above for description)
41121              */
41122             "afteredit" : true,
41123             /**
41124              * @event validateedit
41125              * Fires after a cell is edited, but before the value is set in the record. 
41126          * You can use this to modify the value being set in the field, Return false
41127              * to cancel the change. The edit event object has the following properties <br />
41128              * <ul style="padding:5px;padding-left:16px;">
41129          * <li>editor - This editor</li>
41130              * <li>grid - This grid</li>
41131              * <li>record - The record being edited</li>
41132              * <li>field - The field name being edited</li>
41133              * <li>value - The value being set</li>
41134              * <li>originalValue - The original value for the field, before the edit.</li>
41135              * <li>row - The grid row index</li>
41136              * <li>column - The grid column index</li>
41137              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41138              * </ul>
41139              * @param {Object} e An edit event (see above for description)
41140              */
41141             "validateedit" : true
41142         });
41143     this.on("bodyscroll", this.stopEditing,  this);
41144     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
41145 };
41146
41147 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41148     /**
41149      * @cfg {Number} clicksToEdit
41150      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41151      */
41152     clicksToEdit: 2,
41153
41154     // private
41155     isEditor : true,
41156     // private
41157     trackMouseOver: false, // causes very odd FF errors
41158
41159     onCellDblClick : function(g, row, col){
41160         this.startEditing(row, col);
41161     },
41162
41163     onEditComplete : function(ed, value, startValue){
41164         this.editing = false;
41165         this.activeEditor = null;
41166         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41167         var r = ed.record;
41168         var field = this.colModel.getDataIndex(ed.col);
41169         var e = {
41170             grid: this,
41171             record: r,
41172             field: field,
41173             originalValue: startValue,
41174             value: value,
41175             row: ed.row,
41176             column: ed.col,
41177             cancel:false,
41178             editor: ed
41179         };
41180         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41181         cell.show();
41182           
41183         if(String(value) !== String(startValue)){
41184             
41185             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41186                 r.set(field, e.value);
41187                 // if we are dealing with a combo box..
41188                 // then we also set the 'name' colum to be the displayField
41189                 if (ed.field.displayField && ed.field.name) {
41190                     r.set(ed.field.name, ed.field.el.dom.value);
41191                 }
41192                 
41193                 delete e.cancel; //?? why!!!
41194                 this.fireEvent("afteredit", e);
41195             }
41196         } else {
41197             this.fireEvent("afteredit", e); // always fire it!
41198         }
41199         this.view.focusCell(ed.row, ed.col);
41200     },
41201
41202     /**
41203      * Starts editing the specified for the specified row/column
41204      * @param {Number} rowIndex
41205      * @param {Number} colIndex
41206      */
41207     startEditing : function(row, col){
41208         this.stopEditing();
41209         if(this.colModel.isCellEditable(col, row)){
41210             this.view.ensureVisible(row, col, true);
41211           
41212             var r = this.dataSource.getAt(row);
41213             var field = this.colModel.getDataIndex(col);
41214             var cell = Roo.get(this.view.getCell(row,col));
41215             var e = {
41216                 grid: this,
41217                 record: r,
41218                 field: field,
41219                 value: r.data[field],
41220                 row: row,
41221                 column: col,
41222                 cancel:false 
41223             };
41224             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41225                 this.editing = true;
41226                 var ed = this.colModel.getCellEditor(col, row);
41227                 
41228                 if (!ed) {
41229                     return;
41230                 }
41231                 if(!ed.rendered){
41232                     ed.render(ed.parentEl || document.body);
41233                 }
41234                 ed.field.reset();
41235                
41236                 cell.hide();
41237                 
41238                 (function(){ // complex but required for focus issues in safari, ie and opera
41239                     ed.row = row;
41240                     ed.col = col;
41241                     ed.record = r;
41242                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
41243                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
41244                     this.activeEditor = ed;
41245                     var v = r.data[field];
41246                     ed.startEdit(this.view.getCell(row, col), v);
41247                     // combo's with 'displayField and name set
41248                     if (ed.field.displayField && ed.field.name) {
41249                         ed.field.el.dom.value = r.data[ed.field.name];
41250                     }
41251                     
41252                     
41253                 }).defer(50, this);
41254             }
41255         }
41256     },
41257         
41258     /**
41259      * Stops any active editing
41260      */
41261     stopEditing : function(){
41262         if(this.activeEditor){
41263             this.activeEditor.completeEdit();
41264         }
41265         this.activeEditor = null;
41266     },
41267         
41268          /**
41269      * Called to get grid's drag proxy text, by default returns this.ddText.
41270      * @return {String}
41271      */
41272     getDragDropText : function(){
41273         var count = this.selModel.getSelectedCell() ? 1 : 0;
41274         return String.format(this.ddText, count, count == 1 ? '' : 's');
41275     }
41276         
41277 });/*
41278  * Based on:
41279  * Ext JS Library 1.1.1
41280  * Copyright(c) 2006-2007, Ext JS, LLC.
41281  *
41282  * Originally Released Under LGPL - original licence link has changed is not relivant.
41283  *
41284  * Fork - LGPL
41285  * <script type="text/javascript">
41286  */
41287
41288 // private - not really -- you end up using it !
41289 // This is a support class used internally by the Grid components
41290
41291 /**
41292  * @class Roo.grid.GridEditor
41293  * @extends Roo.Editor
41294  * Class for creating and editable grid elements.
41295  * @param {Object} config any settings (must include field)
41296  */
41297 Roo.grid.GridEditor = function(field, config){
41298     if (!config && field.field) {
41299         config = field;
41300         field = Roo.factory(config.field, Roo.form);
41301     }
41302     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41303     field.monitorTab = false;
41304 };
41305
41306 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41307     
41308     /**
41309      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41310      */
41311     
41312     alignment: "tl-tl",
41313     autoSize: "width",
41314     hideEl : false,
41315     cls: "x-small-editor x-grid-editor",
41316     shim:false,
41317     shadow:"frame"
41318 });/*
41319  * Based on:
41320  * Ext JS Library 1.1.1
41321  * Copyright(c) 2006-2007, Ext JS, LLC.
41322  *
41323  * Originally Released Under LGPL - original licence link has changed is not relivant.
41324  *
41325  * Fork - LGPL
41326  * <script type="text/javascript">
41327  */
41328   
41329
41330   
41331 Roo.grid.PropertyRecord = Roo.data.Record.create([
41332     {name:'name',type:'string'},  'value'
41333 ]);
41334
41335
41336 Roo.grid.PropertyStore = function(grid, source){
41337     this.grid = grid;
41338     this.store = new Roo.data.Store({
41339         recordType : Roo.grid.PropertyRecord
41340     });
41341     this.store.on('update', this.onUpdate,  this);
41342     if(source){
41343         this.setSource(source);
41344     }
41345     Roo.grid.PropertyStore.superclass.constructor.call(this);
41346 };
41347
41348
41349
41350 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41351     setSource : function(o){
41352         this.source = o;
41353         this.store.removeAll();
41354         var data = [];
41355         for(var k in o){
41356             if(this.isEditableValue(o[k])){
41357                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41358             }
41359         }
41360         this.store.loadRecords({records: data}, {}, true);
41361     },
41362
41363     onUpdate : function(ds, record, type){
41364         if(type == Roo.data.Record.EDIT){
41365             var v = record.data['value'];
41366             var oldValue = record.modified['value'];
41367             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41368                 this.source[record.id] = v;
41369                 record.commit();
41370                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41371             }else{
41372                 record.reject();
41373             }
41374         }
41375     },
41376
41377     getProperty : function(row){
41378        return this.store.getAt(row);
41379     },
41380
41381     isEditableValue: function(val){
41382         if(val && val instanceof Date){
41383             return true;
41384         }else if(typeof val == 'object' || typeof val == 'function'){
41385             return false;
41386         }
41387         return true;
41388     },
41389
41390     setValue : function(prop, value){
41391         this.source[prop] = value;
41392         this.store.getById(prop).set('value', value);
41393     },
41394
41395     getSource : function(){
41396         return this.source;
41397     }
41398 });
41399
41400 Roo.grid.PropertyColumnModel = function(grid, store){
41401     this.grid = grid;
41402     var g = Roo.grid;
41403     g.PropertyColumnModel.superclass.constructor.call(this, [
41404         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41405         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41406     ]);
41407     this.store = store;
41408     this.bselect = Roo.DomHelper.append(document.body, {
41409         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41410             {tag: 'option', value: 'true', html: 'true'},
41411             {tag: 'option', value: 'false', html: 'false'}
41412         ]
41413     });
41414     Roo.id(this.bselect);
41415     var f = Roo.form;
41416     this.editors = {
41417         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41418         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41419         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41420         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41421         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41422     };
41423     this.renderCellDelegate = this.renderCell.createDelegate(this);
41424     this.renderPropDelegate = this.renderProp.createDelegate(this);
41425 };
41426
41427 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41428     
41429     
41430     nameText : 'Name',
41431     valueText : 'Value',
41432     
41433     dateFormat : 'm/j/Y',
41434     
41435     
41436     renderDate : function(dateVal){
41437         return dateVal.dateFormat(this.dateFormat);
41438     },
41439
41440     renderBool : function(bVal){
41441         return bVal ? 'true' : 'false';
41442     },
41443
41444     isCellEditable : function(colIndex, rowIndex){
41445         return colIndex == 1;
41446     },
41447
41448     getRenderer : function(col){
41449         return col == 1 ?
41450             this.renderCellDelegate : this.renderPropDelegate;
41451     },
41452
41453     renderProp : function(v){
41454         return this.getPropertyName(v);
41455     },
41456
41457     renderCell : function(val){
41458         var rv = val;
41459         if(val instanceof Date){
41460             rv = this.renderDate(val);
41461         }else if(typeof val == 'boolean'){
41462             rv = this.renderBool(val);
41463         }
41464         return Roo.util.Format.htmlEncode(rv);
41465     },
41466
41467     getPropertyName : function(name){
41468         var pn = this.grid.propertyNames;
41469         return pn && pn[name] ? pn[name] : name;
41470     },
41471
41472     getCellEditor : function(colIndex, rowIndex){
41473         var p = this.store.getProperty(rowIndex);
41474         var n = p.data['name'], val = p.data['value'];
41475         
41476         if(typeof(this.grid.customEditors[n]) == 'string'){
41477             return this.editors[this.grid.customEditors[n]];
41478         }
41479         if(typeof(this.grid.customEditors[n]) != 'undefined'){
41480             return this.grid.customEditors[n];
41481         }
41482         if(val instanceof Date){
41483             return this.editors['date'];
41484         }else if(typeof val == 'number'){
41485             return this.editors['number'];
41486         }else if(typeof val == 'boolean'){
41487             return this.editors['boolean'];
41488         }else{
41489             return this.editors['string'];
41490         }
41491     }
41492 });
41493
41494 /**
41495  * @class Roo.grid.PropertyGrid
41496  * @extends Roo.grid.EditorGrid
41497  * This class represents the  interface of a component based property grid control.
41498  * <br><br>Usage:<pre><code>
41499  var grid = new Roo.grid.PropertyGrid("my-container-id", {
41500       
41501  });
41502  // set any options
41503  grid.render();
41504  * </code></pre>
41505   
41506  * @constructor
41507  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41508  * The container MUST have some type of size defined for the grid to fill. The container will be
41509  * automatically set to position relative if it isn't already.
41510  * @param {Object} config A config object that sets properties on this grid.
41511  */
41512 Roo.grid.PropertyGrid = function(container, config){
41513     config = config || {};
41514     var store = new Roo.grid.PropertyStore(this);
41515     this.store = store;
41516     var cm = new Roo.grid.PropertyColumnModel(this, store);
41517     store.store.sort('name', 'ASC');
41518     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41519         ds: store.store,
41520         cm: cm,
41521         enableColLock:false,
41522         enableColumnMove:false,
41523         stripeRows:false,
41524         trackMouseOver: false,
41525         clicksToEdit:1
41526     }, config));
41527     this.getGridEl().addClass('x-props-grid');
41528     this.lastEditRow = null;
41529     this.on('columnresize', this.onColumnResize, this);
41530     this.addEvents({
41531          /**
41532              * @event beforepropertychange
41533              * Fires before a property changes (return false to stop?)
41534              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41535              * @param {String} id Record Id
41536              * @param {String} newval New Value
41537          * @param {String} oldval Old Value
41538              */
41539         "beforepropertychange": true,
41540         /**
41541              * @event propertychange
41542              * Fires after a property changes
41543              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41544              * @param {String} id Record Id
41545              * @param {String} newval New Value
41546          * @param {String} oldval Old Value
41547              */
41548         "propertychange": true
41549     });
41550     this.customEditors = this.customEditors || {};
41551 };
41552 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41553     
41554      /**
41555      * @cfg {Object} customEditors map of colnames=> custom editors.
41556      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41557      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41558      * false disables editing of the field.
41559          */
41560     
41561       /**
41562      * @cfg {Object} propertyNames map of property Names to their displayed value
41563          */
41564     
41565     render : function(){
41566         Roo.grid.PropertyGrid.superclass.render.call(this);
41567         this.autoSize.defer(100, this);
41568     },
41569
41570     autoSize : function(){
41571         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41572         if(this.view){
41573             this.view.fitColumns();
41574         }
41575     },
41576
41577     onColumnResize : function(){
41578         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41579         this.autoSize();
41580     },
41581     /**
41582      * Sets the data for the Grid
41583      * accepts a Key => Value object of all the elements avaiable.
41584      * @param {Object} data  to appear in grid.
41585      */
41586     setSource : function(source){
41587         this.store.setSource(source);
41588         //this.autoSize();
41589     },
41590     /**
41591      * Gets all the data from the grid.
41592      * @return {Object} data  data stored in grid
41593      */
41594     getSource : function(){
41595         return this.store.getSource();
41596     }
41597 });/*
41598   
41599  * Licence LGPL
41600  
41601  */
41602  
41603 /**
41604  * @class Roo.grid.Calendar
41605  * @extends Roo.grid.Grid
41606  * This class extends the Grid to provide a calendar widget
41607  * <br><br>Usage:<pre><code>
41608  var grid = new Roo.grid.Calendar("my-container-id", {
41609      ds: myDataStore,
41610      cm: myColModel,
41611      selModel: mySelectionModel,
41612      autoSizeColumns: true,
41613      monitorWindowResize: false,
41614      trackMouseOver: true
41615      eventstore : real data store..
41616  });
41617  // set any options
41618  grid.render();
41619   
41620   * @constructor
41621  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41622  * The container MUST have some type of size defined for the grid to fill. The container will be
41623  * automatically set to position relative if it isn't already.
41624  * @param {Object} config A config object that sets properties on this grid.
41625  */
41626 Roo.grid.Calendar = function(container, config){
41627         // initialize the container
41628         this.container = Roo.get(container);
41629         this.container.update("");
41630         this.container.setStyle("overflow", "hidden");
41631     this.container.addClass('x-grid-container');
41632
41633     this.id = this.container.id;
41634
41635     Roo.apply(this, config);
41636     // check and correct shorthanded configs
41637     
41638     var rows = [];
41639     var d =1;
41640     for (var r = 0;r < 6;r++) {
41641         
41642         rows[r]=[];
41643         for (var c =0;c < 7;c++) {
41644             rows[r][c]= '';
41645         }
41646     }
41647     if (this.eventStore) {
41648         this.eventStore= Roo.factory(this.eventStore, Roo.data);
41649         this.eventStore.on('load',this.onLoad, this);
41650         this.eventStore.on('beforeload',this.clearEvents, this);
41651          
41652     }
41653     
41654     this.dataSource = new Roo.data.Store({
41655             proxy: new Roo.data.MemoryProxy(rows),
41656             reader: new Roo.data.ArrayReader({}, [
41657                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41658     });
41659
41660     this.dataSource.load();
41661     this.ds = this.dataSource;
41662     this.ds.xmodule = this.xmodule || false;
41663     
41664     
41665     var cellRender = function(v,x,r)
41666     {
41667         return String.format(
41668             '<div class="fc-day  fc-widget-content"><div>' +
41669                 '<div class="fc-event-container"></div>' +
41670                 '<div class="fc-day-number">{0}</div>'+
41671                 
41672                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41673             '</div></div>', v);
41674     
41675     }
41676     
41677     
41678     this.colModel = new Roo.grid.ColumnModel( [
41679         {
41680             xtype: 'ColumnModel',
41681             xns: Roo.grid,
41682             dataIndex : 'weekday0',
41683             header : 'Sunday',
41684             renderer : cellRender
41685         },
41686         {
41687             xtype: 'ColumnModel',
41688             xns: Roo.grid,
41689             dataIndex : 'weekday1',
41690             header : 'Monday',
41691             renderer : cellRender
41692         },
41693         {
41694             xtype: 'ColumnModel',
41695             xns: Roo.grid,
41696             dataIndex : 'weekday2',
41697             header : 'Tuesday',
41698             renderer : cellRender
41699         },
41700         {
41701             xtype: 'ColumnModel',
41702             xns: Roo.grid,
41703             dataIndex : 'weekday3',
41704             header : 'Wednesday',
41705             renderer : cellRender
41706         },
41707         {
41708             xtype: 'ColumnModel',
41709             xns: Roo.grid,
41710             dataIndex : 'weekday4',
41711             header : 'Thursday',
41712             renderer : cellRender
41713         },
41714         {
41715             xtype: 'ColumnModel',
41716             xns: Roo.grid,
41717             dataIndex : 'weekday5',
41718             header : 'Friday',
41719             renderer : cellRender
41720         },
41721         {
41722             xtype: 'ColumnModel',
41723             xns: Roo.grid,
41724             dataIndex : 'weekday6',
41725             header : 'Saturday',
41726             renderer : cellRender
41727         }
41728     ]);
41729     this.cm = this.colModel;
41730     this.cm.xmodule = this.xmodule || false;
41731  
41732         
41733           
41734     //this.selModel = new Roo.grid.CellSelectionModel();
41735     //this.sm = this.selModel;
41736     //this.selModel.init(this);
41737     
41738     
41739     if(this.width){
41740         this.container.setWidth(this.width);
41741     }
41742
41743     if(this.height){
41744         this.container.setHeight(this.height);
41745     }
41746     /** @private */
41747         this.addEvents({
41748         // raw events
41749         /**
41750          * @event click
41751          * The raw click event for the entire grid.
41752          * @param {Roo.EventObject} e
41753          */
41754         "click" : true,
41755         /**
41756          * @event dblclick
41757          * The raw dblclick event for the entire grid.
41758          * @param {Roo.EventObject} e
41759          */
41760         "dblclick" : true,
41761         /**
41762          * @event contextmenu
41763          * The raw contextmenu event for the entire grid.
41764          * @param {Roo.EventObject} e
41765          */
41766         "contextmenu" : true,
41767         /**
41768          * @event mousedown
41769          * The raw mousedown event for the entire grid.
41770          * @param {Roo.EventObject} e
41771          */
41772         "mousedown" : true,
41773         /**
41774          * @event mouseup
41775          * The raw mouseup event for the entire grid.
41776          * @param {Roo.EventObject} e
41777          */
41778         "mouseup" : true,
41779         /**
41780          * @event mouseover
41781          * The raw mouseover event for the entire grid.
41782          * @param {Roo.EventObject} e
41783          */
41784         "mouseover" : true,
41785         /**
41786          * @event mouseout
41787          * The raw mouseout event for the entire grid.
41788          * @param {Roo.EventObject} e
41789          */
41790         "mouseout" : true,
41791         /**
41792          * @event keypress
41793          * The raw keypress event for the entire grid.
41794          * @param {Roo.EventObject} e
41795          */
41796         "keypress" : true,
41797         /**
41798          * @event keydown
41799          * The raw keydown event for the entire grid.
41800          * @param {Roo.EventObject} e
41801          */
41802         "keydown" : true,
41803
41804         // custom events
41805
41806         /**
41807          * @event cellclick
41808          * Fires when a cell is clicked
41809          * @param {Grid} this
41810          * @param {Number} rowIndex
41811          * @param {Number} columnIndex
41812          * @param {Roo.EventObject} e
41813          */
41814         "cellclick" : true,
41815         /**
41816          * @event celldblclick
41817          * Fires when a cell is double clicked
41818          * @param {Grid} this
41819          * @param {Number} rowIndex
41820          * @param {Number} columnIndex
41821          * @param {Roo.EventObject} e
41822          */
41823         "celldblclick" : true,
41824         /**
41825          * @event rowclick
41826          * Fires when a row is clicked
41827          * @param {Grid} this
41828          * @param {Number} rowIndex
41829          * @param {Roo.EventObject} e
41830          */
41831         "rowclick" : true,
41832         /**
41833          * @event rowdblclick
41834          * Fires when a row is double clicked
41835          * @param {Grid} this
41836          * @param {Number} rowIndex
41837          * @param {Roo.EventObject} e
41838          */
41839         "rowdblclick" : true,
41840         /**
41841          * @event headerclick
41842          * Fires when a header is clicked
41843          * @param {Grid} this
41844          * @param {Number} columnIndex
41845          * @param {Roo.EventObject} e
41846          */
41847         "headerclick" : true,
41848         /**
41849          * @event headerdblclick
41850          * Fires when a header cell is double clicked
41851          * @param {Grid} this
41852          * @param {Number} columnIndex
41853          * @param {Roo.EventObject} e
41854          */
41855         "headerdblclick" : true,
41856         /**
41857          * @event rowcontextmenu
41858          * Fires when a row is right clicked
41859          * @param {Grid} this
41860          * @param {Number} rowIndex
41861          * @param {Roo.EventObject} e
41862          */
41863         "rowcontextmenu" : true,
41864         /**
41865          * @event cellcontextmenu
41866          * Fires when a cell is right clicked
41867          * @param {Grid} this
41868          * @param {Number} rowIndex
41869          * @param {Number} cellIndex
41870          * @param {Roo.EventObject} e
41871          */
41872          "cellcontextmenu" : true,
41873         /**
41874          * @event headercontextmenu
41875          * Fires when a header is right clicked
41876          * @param {Grid} this
41877          * @param {Number} columnIndex
41878          * @param {Roo.EventObject} e
41879          */
41880         "headercontextmenu" : true,
41881         /**
41882          * @event bodyscroll
41883          * Fires when the body element is scrolled
41884          * @param {Number} scrollLeft
41885          * @param {Number} scrollTop
41886          */
41887         "bodyscroll" : true,
41888         /**
41889          * @event columnresize
41890          * Fires when the user resizes a column
41891          * @param {Number} columnIndex
41892          * @param {Number} newSize
41893          */
41894         "columnresize" : true,
41895         /**
41896          * @event columnmove
41897          * Fires when the user moves a column
41898          * @param {Number} oldIndex
41899          * @param {Number} newIndex
41900          */
41901         "columnmove" : true,
41902         /**
41903          * @event startdrag
41904          * Fires when row(s) start being dragged
41905          * @param {Grid} this
41906          * @param {Roo.GridDD} dd The drag drop object
41907          * @param {event} e The raw browser event
41908          */
41909         "startdrag" : true,
41910         /**
41911          * @event enddrag
41912          * Fires when a drag operation is complete
41913          * @param {Grid} this
41914          * @param {Roo.GridDD} dd The drag drop object
41915          * @param {event} e The raw browser event
41916          */
41917         "enddrag" : true,
41918         /**
41919          * @event dragdrop
41920          * Fires when dragged row(s) are dropped on a valid DD target
41921          * @param {Grid} this
41922          * @param {Roo.GridDD} dd The drag drop object
41923          * @param {String} targetId The target drag drop object
41924          * @param {event} e The raw browser event
41925          */
41926         "dragdrop" : true,
41927         /**
41928          * @event dragover
41929          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41930          * @param {Grid} this
41931          * @param {Roo.GridDD} dd The drag drop object
41932          * @param {String} targetId The target drag drop object
41933          * @param {event} e The raw browser event
41934          */
41935         "dragover" : true,
41936         /**
41937          * @event dragenter
41938          *  Fires when the dragged row(s) first cross another DD target while being dragged
41939          * @param {Grid} this
41940          * @param {Roo.GridDD} dd The drag drop object
41941          * @param {String} targetId The target drag drop object
41942          * @param {event} e The raw browser event
41943          */
41944         "dragenter" : true,
41945         /**
41946          * @event dragout
41947          * Fires when the dragged row(s) leave another DD target while being dragged
41948          * @param {Grid} this
41949          * @param {Roo.GridDD} dd The drag drop object
41950          * @param {String} targetId The target drag drop object
41951          * @param {event} e The raw browser event
41952          */
41953         "dragout" : true,
41954         /**
41955          * @event rowclass
41956          * Fires when a row is rendered, so you can change add a style to it.
41957          * @param {GridView} gridview   The grid view
41958          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41959          */
41960         'rowclass' : true,
41961
41962         /**
41963          * @event render
41964          * Fires when the grid is rendered
41965          * @param {Grid} grid
41966          */
41967         'render' : true,
41968             /**
41969              * @event select
41970              * Fires when a date is selected
41971              * @param {DatePicker} this
41972              * @param {Date} date The selected date
41973              */
41974         'select': true,
41975         /**
41976              * @event monthchange
41977              * Fires when the displayed month changes 
41978              * @param {DatePicker} this
41979              * @param {Date} date The selected month
41980              */
41981         'monthchange': true,
41982         /**
41983              * @event evententer
41984              * Fires when mouse over an event
41985              * @param {Calendar} this
41986              * @param {event} Event
41987              */
41988         'evententer': true,
41989         /**
41990              * @event eventleave
41991              * Fires when the mouse leaves an
41992              * @param {Calendar} this
41993              * @param {event}
41994              */
41995         'eventleave': true,
41996         /**
41997              * @event eventclick
41998              * Fires when the mouse click an
41999              * @param {Calendar} this
42000              * @param {event}
42001              */
42002         'eventclick': true,
42003         /**
42004              * @event eventrender
42005              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42006              * @param {Calendar} this
42007              * @param {data} data to be modified
42008              */
42009         'eventrender': true
42010         
42011     });
42012
42013     Roo.grid.Grid.superclass.constructor.call(this);
42014     this.on('render', function() {
42015         this.view.el.addClass('x-grid-cal'); 
42016         
42017         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42018
42019     },this);
42020     
42021     if (!Roo.grid.Calendar.style) {
42022         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42023             
42024             
42025             '.x-grid-cal .x-grid-col' :  {
42026                 height: 'auto !important',
42027                 'vertical-align': 'top'
42028             },
42029             '.x-grid-cal  .fc-event-hori' : {
42030                 height: '14px'
42031             }
42032              
42033             
42034         }, Roo.id());
42035     }
42036
42037     
42038     
42039 };
42040 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42041     /**
42042      * @cfg {Store} eventStore The store that loads events.
42043      */
42044     eventStore : 25,
42045
42046      
42047     activeDate : false,
42048     startDay : 0,
42049     autoWidth : true,
42050     monitorWindowResize : false,
42051
42052     
42053     resizeColumns : function() {
42054         var col = (this.view.el.getWidth() / 7) - 3;
42055         // loop through cols, and setWidth
42056         for(var i =0 ; i < 7 ; i++){
42057             this.cm.setColumnWidth(i, col);
42058         }
42059     },
42060      setDate :function(date) {
42061         
42062         Roo.log('setDate?');
42063         
42064         this.resizeColumns();
42065         var vd = this.activeDate;
42066         this.activeDate = date;
42067 //        if(vd && this.el){
42068 //            var t = date.getTime();
42069 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42070 //                Roo.log('using add remove');
42071 //                
42072 //                this.fireEvent('monthchange', this, date);
42073 //                
42074 //                this.cells.removeClass("fc-state-highlight");
42075 //                this.cells.each(function(c){
42076 //                   if(c.dateValue == t){
42077 //                       c.addClass("fc-state-highlight");
42078 //                       setTimeout(function(){
42079 //                            try{c.dom.firstChild.focus();}catch(e){}
42080 //                       }, 50);
42081 //                       return false;
42082 //                   }
42083 //                   return true;
42084 //                });
42085 //                return;
42086 //            }
42087 //        }
42088         
42089         var days = date.getDaysInMonth();
42090         
42091         var firstOfMonth = date.getFirstDateOfMonth();
42092         var startingPos = firstOfMonth.getDay()-this.startDay;
42093         
42094         if(startingPos < this.startDay){
42095             startingPos += 7;
42096         }
42097         
42098         var pm = date.add(Date.MONTH, -1);
42099         var prevStart = pm.getDaysInMonth()-startingPos;
42100 //        
42101         
42102         
42103         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42104         
42105         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42106         //this.cells.addClassOnOver('fc-state-hover');
42107         
42108         var cells = this.cells.elements;
42109         var textEls = this.textNodes;
42110         
42111         //Roo.each(cells, function(cell){
42112         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42113         //});
42114         
42115         days += startingPos;
42116
42117         // convert everything to numbers so it's fast
42118         var day = 86400000;
42119         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42120         //Roo.log(d);
42121         //Roo.log(pm);
42122         //Roo.log(prevStart);
42123         
42124         var today = new Date().clearTime().getTime();
42125         var sel = date.clearTime().getTime();
42126         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42127         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42128         var ddMatch = this.disabledDatesRE;
42129         var ddText = this.disabledDatesText;
42130         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42131         var ddaysText = this.disabledDaysText;
42132         var format = this.format;
42133         
42134         var setCellClass = function(cal, cell){
42135             
42136             //Roo.log('set Cell Class');
42137             cell.title = "";
42138             var t = d.getTime();
42139             
42140             //Roo.log(d);
42141             
42142             
42143             cell.dateValue = t;
42144             if(t == today){
42145                 cell.className += " fc-today";
42146                 cell.className += " fc-state-highlight";
42147                 cell.title = cal.todayText;
42148             }
42149             if(t == sel){
42150                 // disable highlight in other month..
42151                 cell.className += " fc-state-highlight";
42152                 
42153             }
42154             // disabling
42155             if(t < min) {
42156                 //cell.className = " fc-state-disabled";
42157                 cell.title = cal.minText;
42158                 return;
42159             }
42160             if(t > max) {
42161                 //cell.className = " fc-state-disabled";
42162                 cell.title = cal.maxText;
42163                 return;
42164             }
42165             if(ddays){
42166                 if(ddays.indexOf(d.getDay()) != -1){
42167                     // cell.title = ddaysText;
42168                    // cell.className = " fc-state-disabled";
42169                 }
42170             }
42171             if(ddMatch && format){
42172                 var fvalue = d.dateFormat(format);
42173                 if(ddMatch.test(fvalue)){
42174                     cell.title = ddText.replace("%0", fvalue);
42175                    cell.className = " fc-state-disabled";
42176                 }
42177             }
42178             
42179             if (!cell.initialClassName) {
42180                 cell.initialClassName = cell.dom.className;
42181             }
42182             
42183             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
42184         };
42185
42186         var i = 0;
42187         
42188         for(; i < startingPos; i++) {
42189             cells[i].dayName =  (++prevStart);
42190             Roo.log(textEls[i]);
42191             d.setDate(d.getDate()+1);
42192             
42193             //cells[i].className = "fc-past fc-other-month";
42194             setCellClass(this, cells[i]);
42195         }
42196         
42197         var intDay = 0;
42198         
42199         for(; i < days; i++){
42200             intDay = i - startingPos + 1;
42201             cells[i].dayName =  (intDay);
42202             d.setDate(d.getDate()+1);
42203             
42204             cells[i].className = ''; // "x-date-active";
42205             setCellClass(this, cells[i]);
42206         }
42207         var extraDays = 0;
42208         
42209         for(; i < 42; i++) {
42210             //textEls[i].innerHTML = (++extraDays);
42211             
42212             d.setDate(d.getDate()+1);
42213             cells[i].dayName = (++extraDays);
42214             cells[i].className = "fc-future fc-other-month";
42215             setCellClass(this, cells[i]);
42216         }
42217         
42218         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42219         
42220         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42221         
42222         // this will cause all the cells to mis
42223         var rows= [];
42224         var i =0;
42225         for (var r = 0;r < 6;r++) {
42226             for (var c =0;c < 7;c++) {
42227                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42228             }    
42229         }
42230         
42231         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42232         for(i=0;i<cells.length;i++) {
42233             
42234             this.cells.elements[i].dayName = cells[i].dayName ;
42235             this.cells.elements[i].className = cells[i].className;
42236             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42237             this.cells.elements[i].title = cells[i].title ;
42238             this.cells.elements[i].dateValue = cells[i].dateValue ;
42239         }
42240         
42241         
42242         
42243         
42244         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42245         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42246         
42247         ////if(totalRows != 6){
42248             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42249            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42250        // }
42251         
42252         this.fireEvent('monthchange', this, date);
42253         
42254         
42255     },
42256  /**
42257      * Returns the grid's SelectionModel.
42258      * @return {SelectionModel}
42259      */
42260     getSelectionModel : function(){
42261         if(!this.selModel){
42262             this.selModel = new Roo.grid.CellSelectionModel();
42263         }
42264         return this.selModel;
42265     },
42266
42267     load: function() {
42268         this.eventStore.load()
42269         
42270         
42271         
42272     },
42273     
42274     findCell : function(dt) {
42275         dt = dt.clearTime().getTime();
42276         var ret = false;
42277         this.cells.each(function(c){
42278             //Roo.log("check " +c.dateValue + '?=' + dt);
42279             if(c.dateValue == dt){
42280                 ret = c;
42281                 return false;
42282             }
42283             return true;
42284         });
42285         
42286         return ret;
42287     },
42288     
42289     findCells : function(rec) {
42290         var s = rec.data.start_dt.clone().clearTime().getTime();
42291        // Roo.log(s);
42292         var e= rec.data.end_dt.clone().clearTime().getTime();
42293        // Roo.log(e);
42294         var ret = [];
42295         this.cells.each(function(c){
42296              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42297             
42298             if(c.dateValue > e){
42299                 return ;
42300             }
42301             if(c.dateValue < s){
42302                 return ;
42303             }
42304             ret.push(c);
42305         });
42306         
42307         return ret;    
42308     },
42309     
42310     findBestRow: function(cells)
42311     {
42312         var ret = 0;
42313         
42314         for (var i =0 ; i < cells.length;i++) {
42315             ret  = Math.max(cells[i].rows || 0,ret);
42316         }
42317         return ret;
42318         
42319     },
42320     
42321     
42322     addItem : function(rec)
42323     {
42324         // look for vertical location slot in
42325         var cells = this.findCells(rec);
42326         
42327         rec.row = this.findBestRow(cells);
42328         
42329         // work out the location.
42330         
42331         var crow = false;
42332         var rows = [];
42333         for(var i =0; i < cells.length; i++) {
42334             if (!crow) {
42335                 crow = {
42336                     start : cells[i],
42337                     end :  cells[i]
42338                 };
42339                 continue;
42340             }
42341             if (crow.start.getY() == cells[i].getY()) {
42342                 // on same row.
42343                 crow.end = cells[i];
42344                 continue;
42345             }
42346             // different row.
42347             rows.push(crow);
42348             crow = {
42349                 start: cells[i],
42350                 end : cells[i]
42351             };
42352             
42353         }
42354         
42355         rows.push(crow);
42356         rec.els = [];
42357         rec.rows = rows;
42358         rec.cells = cells;
42359         for (var i = 0; i < cells.length;i++) {
42360             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42361             
42362         }
42363         
42364         
42365     },
42366     
42367     clearEvents: function() {
42368         
42369         if (!this.eventStore.getCount()) {
42370             return;
42371         }
42372         // reset number of rows in cells.
42373         Roo.each(this.cells.elements, function(c){
42374             c.rows = 0;
42375         });
42376         
42377         this.eventStore.each(function(e) {
42378             this.clearEvent(e);
42379         },this);
42380         
42381     },
42382     
42383     clearEvent : function(ev)
42384     {
42385         if (ev.els) {
42386             Roo.each(ev.els, function(el) {
42387                 el.un('mouseenter' ,this.onEventEnter, this);
42388                 el.un('mouseleave' ,this.onEventLeave, this);
42389                 el.remove();
42390             },this);
42391             ev.els = [];
42392         }
42393     },
42394     
42395     
42396     renderEvent : function(ev,ctr) {
42397         if (!ctr) {
42398              ctr = this.view.el.select('.fc-event-container',true).first();
42399         }
42400         
42401          
42402         this.clearEvent(ev);
42403             //code
42404        
42405         
42406         
42407         ev.els = [];
42408         var cells = ev.cells;
42409         var rows = ev.rows;
42410         this.fireEvent('eventrender', this, ev);
42411         
42412         for(var i =0; i < rows.length; i++) {
42413             
42414             cls = '';
42415             if (i == 0) {
42416                 cls += ' fc-event-start';
42417             }
42418             if ((i+1) == rows.length) {
42419                 cls += ' fc-event-end';
42420             }
42421             
42422             //Roo.log(ev.data);
42423             // how many rows should it span..
42424             var cg = this.eventTmpl.append(ctr,Roo.apply({
42425                 fccls : cls
42426                 
42427             }, ev.data) , true);
42428             
42429             
42430             cg.on('mouseenter' ,this.onEventEnter, this, ev);
42431             cg.on('mouseleave' ,this.onEventLeave, this, ev);
42432             cg.on('click', this.onEventClick, this, ev);
42433             
42434             ev.els.push(cg);
42435             
42436             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42437             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42438             //Roo.log(cg);
42439              
42440             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
42441             cg.setWidth(ebox.right - sbox.x -2);
42442         }
42443     },
42444     
42445     renderEvents: function()
42446     {   
42447         // first make sure there is enough space..
42448         
42449         if (!this.eventTmpl) {
42450             this.eventTmpl = new Roo.Template(
42451                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
42452                     '<div class="fc-event-inner">' +
42453                         '<span class="fc-event-time">{time}</span>' +
42454                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42455                     '</div>' +
42456                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
42457                 '</div>'
42458             );
42459                 
42460         }
42461                
42462         
42463         
42464         this.cells.each(function(c) {
42465             //Roo.log(c.select('.fc-day-content div',true).first());
42466             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42467         });
42468         
42469         var ctr = this.view.el.select('.fc-event-container',true).first();
42470         
42471         var cls;
42472         this.eventStore.each(function(ev){
42473             
42474             this.renderEvent(ev);
42475              
42476              
42477         }, this);
42478         this.view.layout();
42479         
42480     },
42481     
42482     onEventEnter: function (e, el,event,d) {
42483         this.fireEvent('evententer', this, el, event);
42484     },
42485     
42486     onEventLeave: function (e, el,event,d) {
42487         this.fireEvent('eventleave', this, el, event);
42488     },
42489     
42490     onEventClick: function (e, el,event,d) {
42491         this.fireEvent('eventclick', this, el, event);
42492     },
42493     
42494     onMonthChange: function () {
42495         this.store.load();
42496     },
42497     
42498     onLoad: function () {
42499         
42500         //Roo.log('calendar onload');
42501 //         
42502         if(this.eventStore.getCount() > 0){
42503             
42504            
42505             
42506             this.eventStore.each(function(d){
42507                 
42508                 
42509                 // FIXME..
42510                 var add =   d.data;
42511                 if (typeof(add.end_dt) == 'undefined')  {
42512                     Roo.log("Missing End time in calendar data: ");
42513                     Roo.log(d);
42514                     return;
42515                 }
42516                 if (typeof(add.start_dt) == 'undefined')  {
42517                     Roo.log("Missing Start time in calendar data: ");
42518                     Roo.log(d);
42519                     return;
42520                 }
42521                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42522                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42523                 add.id = add.id || d.id;
42524                 add.title = add.title || '??';
42525                 
42526                 this.addItem(d);
42527                 
42528              
42529             },this);
42530         }
42531         
42532         this.renderEvents();
42533     }
42534     
42535
42536 });
42537 /*
42538  grid : {
42539                 xtype: 'Grid',
42540                 xns: Roo.grid,
42541                 listeners : {
42542                     render : function ()
42543                     {
42544                         _this.grid = this;
42545                         
42546                         if (!this.view.el.hasClass('course-timesheet')) {
42547                             this.view.el.addClass('course-timesheet');
42548                         }
42549                         if (this.tsStyle) {
42550                             this.ds.load({});
42551                             return; 
42552                         }
42553                         Roo.log('width');
42554                         Roo.log(_this.grid.view.el.getWidth());
42555                         
42556                         
42557                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
42558                             '.course-timesheet .x-grid-row' : {
42559                                 height: '80px'
42560                             },
42561                             '.x-grid-row td' : {
42562                                 'vertical-align' : 0
42563                             },
42564                             '.course-edit-link' : {
42565                                 'color' : 'blue',
42566                                 'text-overflow' : 'ellipsis',
42567                                 'overflow' : 'hidden',
42568                                 'white-space' : 'nowrap',
42569                                 'cursor' : 'pointer'
42570                             },
42571                             '.sub-link' : {
42572                                 'color' : 'green'
42573                             },
42574                             '.de-act-sup-link' : {
42575                                 'color' : 'purple',
42576                                 'text-decoration' : 'line-through'
42577                             },
42578                             '.de-act-link' : {
42579                                 'color' : 'red',
42580                                 'text-decoration' : 'line-through'
42581                             },
42582                             '.course-timesheet .course-highlight' : {
42583                                 'border-top-style': 'dashed !important',
42584                                 'border-bottom-bottom': 'dashed !important'
42585                             },
42586                             '.course-timesheet .course-item' : {
42587                                 'font-family'   : 'tahoma, arial, helvetica',
42588                                 'font-size'     : '11px',
42589                                 'overflow'      : 'hidden',
42590                                 'padding-left'  : '10px',
42591                                 'padding-right' : '10px',
42592                                 'padding-top' : '10px' 
42593                             }
42594                             
42595                         }, Roo.id());
42596                                 this.ds.load({});
42597                     }
42598                 },
42599                 autoWidth : true,
42600                 monitorWindowResize : false,
42601                 cellrenderer : function(v,x,r)
42602                 {
42603                     return v;
42604                 },
42605                 sm : {
42606                     xtype: 'CellSelectionModel',
42607                     xns: Roo.grid
42608                 },
42609                 dataSource : {
42610                     xtype: 'Store',
42611                     xns: Roo.data,
42612                     listeners : {
42613                         beforeload : function (_self, options)
42614                         {
42615                             options.params = options.params || {};
42616                             options.params._month = _this.monthField.getValue();
42617                             options.params.limit = 9999;
42618                             options.params['sort'] = 'when_dt';    
42619                             options.params['dir'] = 'ASC';    
42620                             this.proxy.loadResponse = this.loadResponse;
42621                             Roo.log("load?");
42622                             //this.addColumns();
42623                         },
42624                         load : function (_self, records, options)
42625                         {
42626                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42627                                 // if you click on the translation.. you can edit it...
42628                                 var el = Roo.get(this);
42629                                 var id = el.dom.getAttribute('data-id');
42630                                 var d = el.dom.getAttribute('data-date');
42631                                 var t = el.dom.getAttribute('data-time');
42632                                 //var id = this.child('span').dom.textContent;
42633                                 
42634                                 //Roo.log(this);
42635                                 Pman.Dialog.CourseCalendar.show({
42636                                     id : id,
42637                                     when_d : d,
42638                                     when_t : t,
42639                                     productitem_active : id ? 1 : 0
42640                                 }, function() {
42641                                     _this.grid.ds.load({});
42642                                 });
42643                            
42644                            });
42645                            
42646                            _this.panel.fireEvent('resize', [ '', '' ]);
42647                         }
42648                     },
42649                     loadResponse : function(o, success, response){
42650                             // this is overridden on before load..
42651                             
42652                             Roo.log("our code?");       
42653                             //Roo.log(success);
42654                             //Roo.log(response)
42655                             delete this.activeRequest;
42656                             if(!success){
42657                                 this.fireEvent("loadexception", this, o, response);
42658                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42659                                 return;
42660                             }
42661                             var result;
42662                             try {
42663                                 result = o.reader.read(response);
42664                             }catch(e){
42665                                 Roo.log("load exception?");
42666                                 this.fireEvent("loadexception", this, o, response, e);
42667                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42668                                 return;
42669                             }
42670                             Roo.log("ready...");        
42671                             // loop through result.records;
42672                             // and set this.tdate[date] = [] << array of records..
42673                             _this.tdata  = {};
42674                             Roo.each(result.records, function(r){
42675                                 //Roo.log(r.data);
42676                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42677                                     _this.tdata[r.data.when_dt.format('j')] = [];
42678                                 }
42679                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42680                             });
42681                             
42682                             //Roo.log(_this.tdata);
42683                             
42684                             result.records = [];
42685                             result.totalRecords = 6;
42686                     
42687                             // let's generate some duumy records for the rows.
42688                             //var st = _this.dateField.getValue();
42689                             
42690                             // work out monday..
42691                             //st = st.add(Date.DAY, -1 * st.format('w'));
42692                             
42693                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42694                             
42695                             var firstOfMonth = date.getFirstDayOfMonth();
42696                             var days = date.getDaysInMonth();
42697                             var d = 1;
42698                             var firstAdded = false;
42699                             for (var i = 0; i < result.totalRecords ; i++) {
42700                                 //var d= st.add(Date.DAY, i);
42701                                 var row = {};
42702                                 var added = 0;
42703                                 for(var w = 0 ; w < 7 ; w++){
42704                                     if(!firstAdded && firstOfMonth != w){
42705                                         continue;
42706                                     }
42707                                     if(d > days){
42708                                         continue;
42709                                     }
42710                                     firstAdded = true;
42711                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
42712                                     row['weekday'+w] = String.format(
42713                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
42714                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42715                                                     d,
42716                                                     date.format('Y-m-')+dd
42717                                                 );
42718                                     added++;
42719                                     if(typeof(_this.tdata[d]) != 'undefined'){
42720                                         Roo.each(_this.tdata[d], function(r){
42721                                             var is_sub = '';
42722                                             var deactive = '';
42723                                             var id = r.id;
42724                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
42725                                             if(r.parent_id*1>0){
42726                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
42727                                                 id = r.parent_id;
42728                                             }
42729                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
42730                                                 deactive = 'de-act-link';
42731                                             }
42732                                             
42733                                             row['weekday'+w] += String.format(
42734                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42735                                                     id, //0
42736                                                     r.product_id_name, //1
42737                                                     r.when_dt.format('h:ia'), //2
42738                                                     is_sub, //3
42739                                                     deactive, //4
42740                                                     desc // 5
42741                                             );
42742                                         });
42743                                     }
42744                                     d++;
42745                                 }
42746                                 
42747                                 // only do this if something added..
42748                                 if(added > 0){ 
42749                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
42750                                 }
42751                                 
42752                                 
42753                                 // push it twice. (second one with an hour..
42754                                 
42755                             }
42756                             //Roo.log(result);
42757                             this.fireEvent("load", this, o, o.request.arg);
42758                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42759                         },
42760                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42761                     proxy : {
42762                         xtype: 'HttpProxy',
42763                         xns: Roo.data,
42764                         method : 'GET',
42765                         url : baseURL + '/Roo/Shop_course.php'
42766                     },
42767                     reader : {
42768                         xtype: 'JsonReader',
42769                         xns: Roo.data,
42770                         id : 'id',
42771                         fields : [
42772                             {
42773                                 'name': 'id',
42774                                 'type': 'int'
42775                             },
42776                             {
42777                                 'name': 'when_dt',
42778                                 'type': 'string'
42779                             },
42780                             {
42781                                 'name': 'end_dt',
42782                                 'type': 'string'
42783                             },
42784                             {
42785                                 'name': 'parent_id',
42786                                 'type': 'int'
42787                             },
42788                             {
42789                                 'name': 'product_id',
42790                                 'type': 'int'
42791                             },
42792                             {
42793                                 'name': 'productitem_id',
42794                                 'type': 'int'
42795                             },
42796                             {
42797                                 'name': 'guid',
42798                                 'type': 'int'
42799                             }
42800                         ]
42801                     }
42802                 },
42803                 toolbar : {
42804                     xtype: 'Toolbar',
42805                     xns: Roo,
42806                     items : [
42807                         {
42808                             xtype: 'Button',
42809                             xns: Roo.Toolbar,
42810                             listeners : {
42811                                 click : function (_self, e)
42812                                 {
42813                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42814                                     sd.setMonth(sd.getMonth()-1);
42815                                     _this.monthField.setValue(sd.format('Y-m-d'));
42816                                     _this.grid.ds.load({});
42817                                 }
42818                             },
42819                             text : "Back"
42820                         },
42821                         {
42822                             xtype: 'Separator',
42823                             xns: Roo.Toolbar
42824                         },
42825                         {
42826                             xtype: 'MonthField',
42827                             xns: Roo.form,
42828                             listeners : {
42829                                 render : function (_self)
42830                                 {
42831                                     _this.monthField = _self;
42832                                    // _this.monthField.set  today
42833                                 },
42834                                 select : function (combo, date)
42835                                 {
42836                                     _this.grid.ds.load({});
42837                                 }
42838                             },
42839                             value : (function() { return new Date(); })()
42840                         },
42841                         {
42842                             xtype: 'Separator',
42843                             xns: Roo.Toolbar
42844                         },
42845                         {
42846                             xtype: 'TextItem',
42847                             xns: Roo.Toolbar,
42848                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42849                         },
42850                         {
42851                             xtype: 'Fill',
42852                             xns: Roo.Toolbar
42853                         },
42854                         {
42855                             xtype: 'Button',
42856                             xns: Roo.Toolbar,
42857                             listeners : {
42858                                 click : function (_self, e)
42859                                 {
42860                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42861                                     sd.setMonth(sd.getMonth()+1);
42862                                     _this.monthField.setValue(sd.format('Y-m-d'));
42863                                     _this.grid.ds.load({});
42864                                 }
42865                             },
42866                             text : "Next"
42867                         }
42868                     ]
42869                 },
42870                  
42871             }
42872         };
42873         
42874         *//*
42875  * Based on:
42876  * Ext JS Library 1.1.1
42877  * Copyright(c) 2006-2007, Ext JS, LLC.
42878  *
42879  * Originally Released Under LGPL - original licence link has changed is not relivant.
42880  *
42881  * Fork - LGPL
42882  * <script type="text/javascript">
42883  */
42884  
42885 /**
42886  * @class Roo.LoadMask
42887  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42888  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42889  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42890  * element's UpdateManager load indicator and will be destroyed after the initial load.
42891  * @constructor
42892  * Create a new LoadMask
42893  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42894  * @param {Object} config The config object
42895  */
42896 Roo.LoadMask = function(el, config){
42897     this.el = Roo.get(el);
42898     Roo.apply(this, config);
42899     if(this.store){
42900         this.store.on('beforeload', this.onBeforeLoad, this);
42901         this.store.on('load', this.onLoad, this);
42902         this.store.on('loadexception', this.onLoadException, this);
42903         this.removeMask = false;
42904     }else{
42905         var um = this.el.getUpdateManager();
42906         um.showLoadIndicator = false; // disable the default indicator
42907         um.on('beforeupdate', this.onBeforeLoad, this);
42908         um.on('update', this.onLoad, this);
42909         um.on('failure', this.onLoad, this);
42910         this.removeMask = true;
42911     }
42912 };
42913
42914 Roo.LoadMask.prototype = {
42915     /**
42916      * @cfg {Boolean} removeMask
42917      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42918      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42919      */
42920     removeMask : false,
42921     /**
42922      * @cfg {String} msg
42923      * The text to display in a centered loading message box (defaults to 'Loading...')
42924      */
42925     msg : 'Loading...',
42926     /**
42927      * @cfg {String} msgCls
42928      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42929      */
42930     msgCls : 'x-mask-loading',
42931
42932     /**
42933      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42934      * @type Boolean
42935      */
42936     disabled: false,
42937
42938     /**
42939      * Disables the mask to prevent it from being displayed
42940      */
42941     disable : function(){
42942        this.disabled = true;
42943     },
42944
42945     /**
42946      * Enables the mask so that it can be displayed
42947      */
42948     enable : function(){
42949         this.disabled = false;
42950     },
42951     
42952     onLoadException : function()
42953     {
42954         Roo.log(arguments);
42955         
42956         if (typeof(arguments[3]) != 'undefined') {
42957             Roo.MessageBox.alert("Error loading",arguments[3]);
42958         } 
42959         /*
42960         try {
42961             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42962                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42963             }   
42964         } catch(e) {
42965             
42966         }
42967         */
42968     
42969         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
42970     },
42971     // private
42972     onLoad : function()
42973     {
42974         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
42975     },
42976
42977     // private
42978     onBeforeLoad : function(){
42979         if(!this.disabled){
42980             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
42981         }
42982     },
42983
42984     // private
42985     destroy : function(){
42986         if(this.store){
42987             this.store.un('beforeload', this.onBeforeLoad, this);
42988             this.store.un('load', this.onLoad, this);
42989             this.store.un('loadexception', this.onLoadException, this);
42990         }else{
42991             var um = this.el.getUpdateManager();
42992             um.un('beforeupdate', this.onBeforeLoad, this);
42993             um.un('update', this.onLoad, this);
42994             um.un('failure', this.onLoad, this);
42995         }
42996     }
42997 };/*
42998  * Based on:
42999  * Ext JS Library 1.1.1
43000  * Copyright(c) 2006-2007, Ext JS, LLC.
43001  *
43002  * Originally Released Under LGPL - original licence link has changed is not relivant.
43003  *
43004  * Fork - LGPL
43005  * <script type="text/javascript">
43006  */
43007
43008
43009 /**
43010  * @class Roo.XTemplate
43011  * @extends Roo.Template
43012  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43013 <pre><code>
43014 var t = new Roo.XTemplate(
43015         '&lt;select name="{name}"&gt;',
43016                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
43017         '&lt;/select&gt;'
43018 );
43019  
43020 // then append, applying the master template values
43021  </code></pre>
43022  *
43023  * Supported features:
43024  *
43025  *  Tags:
43026
43027 <pre><code>
43028       {a_variable} - output encoded.
43029       {a_variable.format:("Y-m-d")} - call a method on the variable
43030       {a_variable:raw} - unencoded output
43031       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43032       {a_variable:this.method_on_template(...)} - call a method on the template object.
43033  
43034 </code></pre>
43035  *  The tpl tag:
43036 <pre><code>
43037         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
43038         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
43039         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
43040         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
43041   
43042         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
43043         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
43044 </code></pre>
43045  *      
43046  */
43047 Roo.XTemplate = function()
43048 {
43049     Roo.XTemplate.superclass.constructor.apply(this, arguments);
43050     if (this.html) {
43051         this.compile();
43052     }
43053 };
43054
43055
43056 Roo.extend(Roo.XTemplate, Roo.Template, {
43057
43058     /**
43059      * The various sub templates
43060      */
43061     tpls : false,
43062     /**
43063      *
43064      * basic tag replacing syntax
43065      * WORD:WORD()
43066      *
43067      * // you can fake an object call by doing this
43068      *  x.t:(test,tesT) 
43069      * 
43070      */
43071     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43072
43073     /**
43074      * compile the template
43075      *
43076      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43077      *
43078      */
43079     compile: function()
43080     {
43081         var s = this.html;
43082      
43083         s = ['<tpl>', s, '</tpl>'].join('');
43084     
43085         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43086             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43087             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
43088             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43089             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
43090             m,
43091             id     = 0,
43092             tpls   = [];
43093     
43094         while(true == !!(m = s.match(re))){
43095             var forMatch   = m[0].match(nameRe),
43096                 ifMatch   = m[0].match(ifRe),
43097                 execMatch   = m[0].match(execRe),
43098                 namedMatch   = m[0].match(namedRe),
43099                 
43100                 exp  = null, 
43101                 fn   = null,
43102                 exec = null,
43103                 name = forMatch && forMatch[1] ? forMatch[1] : '';
43104                 
43105             if (ifMatch) {
43106                 // if - puts fn into test..
43107                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43108                 if(exp){
43109                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43110                 }
43111             }
43112             
43113             if (execMatch) {
43114                 // exec - calls a function... returns empty if true is  returned.
43115                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43116                 if(exp){
43117                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43118                 }
43119             }
43120             
43121             
43122             if (name) {
43123                 // for = 
43124                 switch(name){
43125                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43126                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43127                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43128                 }
43129             }
43130             var uid = namedMatch ? namedMatch[1] : id;
43131             
43132             
43133             tpls.push({
43134                 id:     namedMatch ? namedMatch[1] : id,
43135                 target: name,
43136                 exec:   exec,
43137                 test:   fn,
43138                 body:   m[1] || ''
43139             });
43140             if (namedMatch) {
43141                 s = s.replace(m[0], '');
43142             } else { 
43143                 s = s.replace(m[0], '{xtpl'+ id + '}');
43144             }
43145             ++id;
43146         }
43147         this.tpls = [];
43148         for(var i = tpls.length-1; i >= 0; --i){
43149             this.compileTpl(tpls[i]);
43150             this.tpls[tpls[i].id] = tpls[i];
43151         }
43152         this.master = tpls[tpls.length-1];
43153         return this;
43154     },
43155     /**
43156      * same as applyTemplate, except it's done to one of the subTemplates
43157      * when using named templates, you can do:
43158      *
43159      * var str = pl.applySubTemplate('your-name', values);
43160      *
43161      * 
43162      * @param {Number} id of the template
43163      * @param {Object} values to apply to template
43164      * @param {Object} parent (normaly the instance of this object)
43165      */
43166     applySubTemplate : function(id, values, parent)
43167     {
43168         
43169         
43170         var t = this.tpls[id];
43171         
43172         
43173         try { 
43174             if(t.test && !t.test.call(this, values, parent)){
43175                 return '';
43176             }
43177         } catch(e) {
43178             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43179             Roo.log(e.toString());
43180             Roo.log(t.test);
43181             return ''
43182         }
43183         try { 
43184             
43185             if(t.exec && t.exec.call(this, values, parent)){
43186                 return '';
43187             }
43188         } catch(e) {
43189             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43190             Roo.log(e.toString());
43191             Roo.log(t.exec);
43192             return ''
43193         }
43194         try {
43195             var vs = t.target ? t.target.call(this, values, parent) : values;
43196             parent = t.target ? values : parent;
43197             if(t.target && vs instanceof Array){
43198                 var buf = [];
43199                 for(var i = 0, len = vs.length; i < len; i++){
43200                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
43201                 }
43202                 return buf.join('');
43203             }
43204             return t.compiled.call(this, vs, parent);
43205         } catch (e) {
43206             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43207             Roo.log(e.toString());
43208             Roo.log(t.compiled);
43209             return '';
43210         }
43211     },
43212
43213     compileTpl : function(tpl)
43214     {
43215         var fm = Roo.util.Format;
43216         var useF = this.disableFormats !== true;
43217         var sep = Roo.isGecko ? "+" : ",";
43218         var undef = function(str) {
43219             Roo.log("Property not found :"  + str);
43220             return '';
43221         };
43222         
43223         var fn = function(m, name, format, args)
43224         {
43225             //Roo.log(arguments);
43226             args = args ? args.replace(/\\'/g,"'") : args;
43227             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43228             if (typeof(format) == 'undefined') {
43229                 format= 'htmlEncode';
43230             }
43231             if (format == 'raw' ) {
43232                 format = false;
43233             }
43234             
43235             if(name.substr(0, 4) == 'xtpl'){
43236                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43237             }
43238             
43239             // build an array of options to determine if value is undefined..
43240             
43241             // basically get 'xxxx.yyyy' then do
43242             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43243             //    (function () { Roo.log("Property not found"); return ''; })() :
43244             //    ......
43245             
43246             var udef_ar = [];
43247             var lookfor = '';
43248             Roo.each(name.split('.'), function(st) {
43249                 lookfor += (lookfor.length ? '.': '') + st;
43250                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
43251             });
43252             
43253             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43254             
43255             
43256             if(format && useF){
43257                 
43258                 args = args ? ',' + args : "";
43259                  
43260                 if(format.substr(0, 5) != "this."){
43261                     format = "fm." + format + '(';
43262                 }else{
43263                     format = 'this.call("'+ format.substr(5) + '", ';
43264                     args = ", values";
43265                 }
43266                 
43267                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
43268             }
43269              
43270             if (args.length) {
43271                 // called with xxyx.yuu:(test,test)
43272                 // change to ()
43273                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
43274             }
43275             // raw.. - :raw modifier..
43276             return "'"+ sep + udef_st  + name + ")"+sep+"'";
43277             
43278         };
43279         var body;
43280         // branched to use + in gecko and [].join() in others
43281         if(Roo.isGecko){
43282             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
43283                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43284                     "';};};";
43285         }else{
43286             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
43287             body.push(tpl.body.replace(/(\r\n|\n)/g,
43288                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43289             body.push("'].join('');};};");
43290             body = body.join('');
43291         }
43292         
43293         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43294        
43295         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
43296         eval(body);
43297         
43298         return this;
43299     },
43300
43301     applyTemplate : function(values){
43302         return this.master.compiled.call(this, values, {});
43303         //var s = this.subs;
43304     },
43305
43306     apply : function(){
43307         return this.applyTemplate.apply(this, arguments);
43308     }
43309
43310  });
43311
43312 Roo.XTemplate.from = function(el){
43313     el = Roo.getDom(el);
43314     return new Roo.XTemplate(el.value || el.innerHTML);
43315 };