fix dialog on htmleditor link
[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               
10416                 dlg.on("hide", handleHide);
10417                 mask = dlg.mask;
10418                 dlg.addKeyListener(27, handleEsc);
10419                 buttons = {};
10420                 var bt = this.buttonText;
10421                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10422                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10423                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10424                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10425                 bodyEl = dlg.body.createChild({
10426
10427                     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>'
10428                 });
10429                 msgEl = bodyEl.dom.firstChild;
10430                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10431                 textboxEl.enableDisplayMode();
10432                 textboxEl.addKeyListener([10,13], function(){
10433                     if(dlg.isVisible() && opt && opt.buttons){
10434                         if(opt.buttons.ok){
10435                             handleButton("ok");
10436                         }else if(opt.buttons.yes){
10437                             handleButton("yes");
10438                         }
10439                     }
10440                 });
10441                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10442                 textareaEl.enableDisplayMode();
10443                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10444                 progressEl.enableDisplayMode();
10445                 var pf = progressEl.dom.firstChild;
10446                 if (pf) {
10447                     pp = Roo.get(pf.firstChild);
10448                     pp.setHeight(pf.offsetHeight);
10449                 }
10450                 
10451             }
10452             return dlg;
10453         },
10454
10455         /**
10456          * Updates the message box body text
10457          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10458          * the XHTML-compliant non-breaking space character '&amp;#160;')
10459          * @return {Roo.MessageBox} This message box
10460          */
10461         updateText : function(text){
10462             if(!dlg.isVisible() && !opt.width){
10463                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10464             }
10465             msgEl.innerHTML = text || '&#160;';
10466       
10467             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10468             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10469             var w = Math.max(
10470                     Math.min(opt.width || cw , this.maxWidth), 
10471                     Math.max(opt.minWidth || this.minWidth, bwidth)
10472             );
10473             if(opt.prompt){
10474                 activeTextEl.setWidth(w);
10475             }
10476             if(dlg.isVisible()){
10477                 dlg.fixedcenter = false;
10478             }
10479             // to big, make it scroll. = But as usual stupid IE does not support
10480             // !important..
10481             
10482             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10483                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10484                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10485             } else {
10486                 bodyEl.dom.style.height = '';
10487                 bodyEl.dom.style.overflowY = '';
10488             }
10489             if (cw > w) {
10490                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10491             } else {
10492                 bodyEl.dom.style.overflowX = '';
10493             }
10494             
10495             dlg.setContentSize(w, bodyEl.getHeight());
10496             if(dlg.isVisible()){
10497                 dlg.fixedcenter = true;
10498             }
10499             return this;
10500         },
10501
10502         /**
10503          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10504          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10505          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10506          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10507          * @return {Roo.MessageBox} This message box
10508          */
10509         updateProgress : function(value, text){
10510             if(text){
10511                 this.updateText(text);
10512             }
10513             if (pp) { // weird bug on my firefox - for some reason this is not defined
10514                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10515             }
10516             return this;
10517         },        
10518
10519         /**
10520          * Returns true if the message box is currently displayed
10521          * @return {Boolean} True if the message box is visible, else false
10522          */
10523         isVisible : function(){
10524             return dlg && dlg.isVisible();  
10525         },
10526
10527         /**
10528          * Hides the message box if it is displayed
10529          */
10530         hide : function(){
10531             if(this.isVisible()){
10532                 dlg.hide();
10533             }  
10534         },
10535
10536         /**
10537          * Displays a new message box, or reinitializes an existing message box, based on the config options
10538          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10539          * The following config object properties are supported:
10540          * <pre>
10541 Property    Type             Description
10542 ----------  ---------------  ------------------------------------------------------------------------------------
10543 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10544                                    closes (defaults to undefined)
10545 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10546                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10547 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10548                                    progress and wait dialogs will ignore this property and always hide the
10549                                    close button as they can only be closed programmatically.
10550 cls               String           A custom CSS class to apply to the message box element
10551 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10552                                    displayed (defaults to 75)
10553 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10554                                    function will be btn (the name of the button that was clicked, if applicable,
10555                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10556                                    Progress and wait dialogs will ignore this option since they do not respond to
10557                                    user actions and can only be closed programmatically, so any required function
10558                                    should be called by the same code after it closes the dialog.
10559 icon              String           A CSS class that provides a background image to be used as an icon for
10560                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10561 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10562 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10563 modal             Boolean          False to allow user interaction with the page while the message box is
10564                                    displayed (defaults to true)
10565 msg               String           A string that will replace the existing message box body text (defaults
10566                                    to the XHTML-compliant non-breaking space character '&#160;')
10567 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10568 progress          Boolean          True to display a progress bar (defaults to false)
10569 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10570 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10571 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10572 title             String           The title text
10573 value             String           The string value to set into the active textbox element if displayed
10574 wait              Boolean          True to display a progress bar (defaults to false)
10575 width             Number           The width of the dialog in pixels
10576 </pre>
10577          *
10578          * Example usage:
10579          * <pre><code>
10580 Roo.Msg.show({
10581    title: 'Address',
10582    msg: 'Please enter your address:',
10583    width: 300,
10584    buttons: Roo.MessageBox.OKCANCEL,
10585    multiline: true,
10586    fn: saveAddress,
10587    animEl: 'addAddressBtn'
10588 });
10589 </code></pre>
10590          * @param {Object} config Configuration options
10591          * @return {Roo.MessageBox} This message box
10592          */
10593         show : function(options)
10594         {
10595             
10596             // this causes nightmares if you show one dialog after another
10597             // especially on callbacks..
10598              
10599             if(this.isVisible()){
10600                 
10601                 this.hide();
10602                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10603                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10604                 Roo.log("New Dialog Message:" +  options.msg )
10605                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10606                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10607                 
10608             }
10609             var d = this.getDialog();
10610             opt = options;
10611             d.setTitle(opt.title || "&#160;");
10612             d.close.setDisplayed(opt.closable !== false);
10613             activeTextEl = textboxEl;
10614             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10615             if(opt.prompt){
10616                 if(opt.multiline){
10617                     textboxEl.hide();
10618                     textareaEl.show();
10619                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10620                         opt.multiline : this.defaultTextHeight);
10621                     activeTextEl = textareaEl;
10622                 }else{
10623                     textboxEl.show();
10624                     textareaEl.hide();
10625                 }
10626             }else{
10627                 textboxEl.hide();
10628                 textareaEl.hide();
10629             }
10630             progressEl.setDisplayed(opt.progress === true);
10631             this.updateProgress(0);
10632             activeTextEl.dom.value = opt.value || "";
10633             if(opt.prompt){
10634                 dlg.setDefaultButton(activeTextEl);
10635             }else{
10636                 var bs = opt.buttons;
10637                 var db = null;
10638                 if(bs && bs.ok){
10639                     db = buttons["ok"];
10640                 }else if(bs && bs.yes){
10641                     db = buttons["yes"];
10642                 }
10643                 dlg.setDefaultButton(db);
10644             }
10645             bwidth = updateButtons(opt.buttons);
10646             this.updateText(opt.msg);
10647             if(opt.cls){
10648                 d.el.addClass(opt.cls);
10649             }
10650             d.proxyDrag = opt.proxyDrag === true;
10651             d.modal = opt.modal !== false;
10652             d.mask = opt.modal !== false ? mask : false;
10653             if(!d.isVisible()){
10654                 // force it to the end of the z-index stack so it gets a cursor in FF
10655                 document.body.appendChild(dlg.el.dom);
10656                 d.animateTarget = null;
10657                 d.show(options.animEl);
10658             }
10659             dlg.toFront();
10660             return this;
10661         },
10662
10663         /**
10664          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10665          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10666          * and closing the message box when the process is complete.
10667          * @param {String} title The title bar text
10668          * @param {String} msg The message box body text
10669          * @return {Roo.MessageBox} This message box
10670          */
10671         progress : function(title, msg){
10672             this.show({
10673                 title : title,
10674                 msg : msg,
10675                 buttons: false,
10676                 progress:true,
10677                 closable:false,
10678                 minWidth: this.minProgressWidth,
10679                 modal : true
10680             });
10681             return this;
10682         },
10683
10684         /**
10685          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10686          * If a callback function is passed it will be called after the user clicks the button, and the
10687          * id of the button that was clicked will be passed as the only parameter to the callback
10688          * (could also be the top-right close button).
10689          * @param {String} title The title bar text
10690          * @param {String} msg The message box body text
10691          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10692          * @param {Object} scope (optional) The scope of the callback function
10693          * @return {Roo.MessageBox} This message box
10694          */
10695         alert : function(title, msg, fn, scope){
10696             this.show({
10697                 title : title,
10698                 msg : msg,
10699                 buttons: this.OK,
10700                 fn: fn,
10701                 scope : scope,
10702                 modal : true
10703             });
10704             return this;
10705         },
10706
10707         /**
10708          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10709          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10710          * You are responsible for closing the message box when the process is complete.
10711          * @param {String} msg The message box body text
10712          * @param {String} title (optional) The title bar text
10713          * @return {Roo.MessageBox} This message box
10714          */
10715         wait : function(msg, title){
10716             this.show({
10717                 title : title,
10718                 msg : msg,
10719                 buttons: false,
10720                 closable:false,
10721                 progress:true,
10722                 modal:true,
10723                 width:300,
10724                 wait:true
10725             });
10726             waitTimer = Roo.TaskMgr.start({
10727                 run: function(i){
10728                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10729                 },
10730                 interval: 1000
10731             });
10732             return this;
10733         },
10734
10735         /**
10736          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10737          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10738          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10739          * @param {String} title The title bar text
10740          * @param {String} msg The message box body text
10741          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10742          * @param {Object} scope (optional) The scope of the callback function
10743          * @return {Roo.MessageBox} This message box
10744          */
10745         confirm : function(title, msg, fn, scope){
10746             this.show({
10747                 title : title,
10748                 msg : msg,
10749                 buttons: this.YESNO,
10750                 fn: fn,
10751                 scope : scope,
10752                 modal : true
10753             });
10754             return this;
10755         },
10756
10757         /**
10758          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10759          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10760          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10761          * (could also be the top-right close button) and the text that was entered will be passed as the two
10762          * parameters to the callback.
10763          * @param {String} title The title bar text
10764          * @param {String} msg The message box body text
10765          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10766          * @param {Object} scope (optional) The scope of the callback function
10767          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10768          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10769          * @return {Roo.MessageBox} This message box
10770          */
10771         prompt : function(title, msg, fn, scope, multiline){
10772             this.show({
10773                 title : title,
10774                 msg : msg,
10775                 buttons: this.OKCANCEL,
10776                 fn: fn,
10777                 minWidth:250,
10778                 scope : scope,
10779                 prompt:true,
10780                 multiline: multiline,
10781                 modal : true
10782             });
10783             return this;
10784         },
10785
10786         /**
10787          * Button config that displays a single OK button
10788          * @type Object
10789          */
10790         OK : {ok:true},
10791         /**
10792          * Button config that displays Yes and No buttons
10793          * @type Object
10794          */
10795         YESNO : {yes:true, no:true},
10796         /**
10797          * Button config that displays OK and Cancel buttons
10798          * @type Object
10799          */
10800         OKCANCEL : {ok:true, cancel:true},
10801         /**
10802          * Button config that displays Yes, No and Cancel buttons
10803          * @type Object
10804          */
10805         YESNOCANCEL : {yes:true, no:true, cancel:true},
10806
10807         /**
10808          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10809          * @type Number
10810          */
10811         defaultTextHeight : 75,
10812         /**
10813          * The maximum width in pixels of the message box (defaults to 600)
10814          * @type Number
10815          */
10816         maxWidth : 600,
10817         /**
10818          * The minimum width in pixels of the message box (defaults to 100)
10819          * @type Number
10820          */
10821         minWidth : 100,
10822         /**
10823          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10824          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10825          * @type Number
10826          */
10827         minProgressWidth : 250,
10828         /**
10829          * An object containing the default button text strings that can be overriden for localized language support.
10830          * Supported properties are: ok, cancel, yes and no.
10831          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10832          * @type Object
10833          */
10834         buttonText : {
10835             ok : "OK",
10836             cancel : "Cancel",
10837             yes : "Yes",
10838             no : "No"
10839         }
10840     };
10841 }();
10842
10843 /**
10844  * Shorthand for {@link Roo.MessageBox}
10845  */
10846 Roo.Msg = Roo.MessageBox;/*
10847  * Based on:
10848  * Ext JS Library 1.1.1
10849  * Copyright(c) 2006-2007, Ext JS, LLC.
10850  *
10851  * Originally Released Under LGPL - original licence link has changed is not relivant.
10852  *
10853  * Fork - LGPL
10854  * <script type="text/javascript">
10855  */
10856 /**
10857  * @class Roo.QuickTips
10858  * Provides attractive and customizable tooltips for any element.
10859  * @static
10860  */
10861 Roo.QuickTips = function(){
10862     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10863     var ce, bd, xy, dd;
10864     var visible = false, disabled = true, inited = false;
10865     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10866     
10867     var onOver = function(e){
10868         if(disabled){
10869             return;
10870         }
10871         var t = e.getTarget();
10872         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10873             return;
10874         }
10875         if(ce && t == ce.el){
10876             clearTimeout(hideProc);
10877             return;
10878         }
10879         if(t && tagEls[t.id]){
10880             tagEls[t.id].el = t;
10881             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10882             return;
10883         }
10884         var ttp, et = Roo.fly(t);
10885         var ns = cfg.namespace;
10886         if(tm.interceptTitles && t.title){
10887             ttp = t.title;
10888             t.qtip = ttp;
10889             t.removeAttribute("title");
10890             e.preventDefault();
10891         }else{
10892             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10893         }
10894         if(ttp){
10895             showProc = show.defer(tm.showDelay, tm, [{
10896                 el: t, 
10897                 text: ttp.replace(/\\n/g,'<br/>'),
10898                 width: et.getAttributeNS(ns, cfg.width),
10899                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10900                 title: et.getAttributeNS(ns, cfg.title),
10901                     cls: et.getAttributeNS(ns, cfg.cls)
10902             }]);
10903         }
10904     };
10905     
10906     var onOut = function(e){
10907         clearTimeout(showProc);
10908         var t = e.getTarget();
10909         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10910             hideProc = setTimeout(hide, tm.hideDelay);
10911         }
10912     };
10913     
10914     var onMove = function(e){
10915         if(disabled){
10916             return;
10917         }
10918         xy = e.getXY();
10919         xy[1] += 18;
10920         if(tm.trackMouse && ce){
10921             el.setXY(xy);
10922         }
10923     };
10924     
10925     var onDown = function(e){
10926         clearTimeout(showProc);
10927         clearTimeout(hideProc);
10928         if(!e.within(el)){
10929             if(tm.hideOnClick){
10930                 hide();
10931                 tm.disable();
10932                 tm.enable.defer(100, tm);
10933             }
10934         }
10935     };
10936     
10937     var getPad = function(){
10938         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10939     };
10940
10941     var show = function(o){
10942         if(disabled){
10943             return;
10944         }
10945         clearTimeout(dismissProc);
10946         ce = o;
10947         if(removeCls){ // in case manually hidden
10948             el.removeClass(removeCls);
10949             removeCls = null;
10950         }
10951         if(ce.cls){
10952             el.addClass(ce.cls);
10953             removeCls = ce.cls;
10954         }
10955         if(ce.title){
10956             tipTitle.update(ce.title);
10957             tipTitle.show();
10958         }else{
10959             tipTitle.update('');
10960             tipTitle.hide();
10961         }
10962         el.dom.style.width  = tm.maxWidth+'px';
10963         //tipBody.dom.style.width = '';
10964         tipBodyText.update(o.text);
10965         var p = getPad(), w = ce.width;
10966         if(!w){
10967             var td = tipBodyText.dom;
10968             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10969             if(aw > tm.maxWidth){
10970                 w = tm.maxWidth;
10971             }else if(aw < tm.minWidth){
10972                 w = tm.minWidth;
10973             }else{
10974                 w = aw;
10975             }
10976         }
10977         //tipBody.setWidth(w);
10978         el.setWidth(parseInt(w, 10) + p);
10979         if(ce.autoHide === false){
10980             close.setDisplayed(true);
10981             if(dd){
10982                 dd.unlock();
10983             }
10984         }else{
10985             close.setDisplayed(false);
10986             if(dd){
10987                 dd.lock();
10988             }
10989         }
10990         if(xy){
10991             el.avoidY = xy[1]-18;
10992             el.setXY(xy);
10993         }
10994         if(tm.animate){
10995             el.setOpacity(.1);
10996             el.setStyle("visibility", "visible");
10997             el.fadeIn({callback: afterShow});
10998         }else{
10999             afterShow();
11000         }
11001     };
11002     
11003     var afterShow = function(){
11004         if(ce){
11005             el.show();
11006             esc.enable();
11007             if(tm.autoDismiss && ce.autoHide !== false){
11008                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11009             }
11010         }
11011     };
11012     
11013     var hide = function(noanim){
11014         clearTimeout(dismissProc);
11015         clearTimeout(hideProc);
11016         ce = null;
11017         if(el.isVisible()){
11018             esc.disable();
11019             if(noanim !== true && tm.animate){
11020                 el.fadeOut({callback: afterHide});
11021             }else{
11022                 afterHide();
11023             } 
11024         }
11025     };
11026     
11027     var afterHide = function(){
11028         el.hide();
11029         if(removeCls){
11030             el.removeClass(removeCls);
11031             removeCls = null;
11032         }
11033     };
11034     
11035     return {
11036         /**
11037         * @cfg {Number} minWidth
11038         * The minimum width of the quick tip (defaults to 40)
11039         */
11040        minWidth : 40,
11041         /**
11042         * @cfg {Number} maxWidth
11043         * The maximum width of the quick tip (defaults to 300)
11044         */
11045        maxWidth : 300,
11046         /**
11047         * @cfg {Boolean} interceptTitles
11048         * True to automatically use the element's DOM title value if available (defaults to false)
11049         */
11050        interceptTitles : false,
11051         /**
11052         * @cfg {Boolean} trackMouse
11053         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11054         */
11055        trackMouse : false,
11056         /**
11057         * @cfg {Boolean} hideOnClick
11058         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11059         */
11060        hideOnClick : true,
11061         /**
11062         * @cfg {Number} showDelay
11063         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11064         */
11065        showDelay : 500,
11066         /**
11067         * @cfg {Number} hideDelay
11068         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11069         */
11070        hideDelay : 200,
11071         /**
11072         * @cfg {Boolean} autoHide
11073         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11074         * Used in conjunction with hideDelay.
11075         */
11076        autoHide : true,
11077         /**
11078         * @cfg {Boolean}
11079         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11080         * (defaults to true).  Used in conjunction with autoDismissDelay.
11081         */
11082        autoDismiss : true,
11083         /**
11084         * @cfg {Number}
11085         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11086         */
11087        autoDismissDelay : 5000,
11088        /**
11089         * @cfg {Boolean} animate
11090         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11091         */
11092        animate : false,
11093
11094        /**
11095         * @cfg {String} title
11096         * Title text to display (defaults to '').  This can be any valid HTML markup.
11097         */
11098         title: '',
11099        /**
11100         * @cfg {String} text
11101         * Body text to display (defaults to '').  This can be any valid HTML markup.
11102         */
11103         text : '',
11104        /**
11105         * @cfg {String} cls
11106         * A CSS class to apply to the base quick tip element (defaults to '').
11107         */
11108         cls : '',
11109        /**
11110         * @cfg {Number} width
11111         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11112         * minWidth or maxWidth.
11113         */
11114         width : null,
11115
11116     /**
11117      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11118      * or display QuickTips in a page.
11119      */
11120        init : function(){
11121           tm = Roo.QuickTips;
11122           cfg = tm.tagConfig;
11123           if(!inited){
11124               if(!Roo.isReady){ // allow calling of init() before onReady
11125                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11126                   return;
11127               }
11128               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11129               el.fxDefaults = {stopFx: true};
11130               // maximum custom styling
11131               //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>');
11132               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>');              
11133               tipTitle = el.child('h3');
11134               tipTitle.enableDisplayMode("block");
11135               tipBody = el.child('div.x-tip-bd');
11136               tipBodyText = el.child('div.x-tip-bd-inner');
11137               //bdLeft = el.child('div.x-tip-bd-left');
11138               //bdRight = el.child('div.x-tip-bd-right');
11139               close = el.child('div.x-tip-close');
11140               close.enableDisplayMode("block");
11141               close.on("click", hide);
11142               var d = Roo.get(document);
11143               d.on("mousedown", onDown);
11144               d.on("mouseover", onOver);
11145               d.on("mouseout", onOut);
11146               d.on("mousemove", onMove);
11147               esc = d.addKeyListener(27, hide);
11148               esc.disable();
11149               if(Roo.dd.DD){
11150                   dd = el.initDD("default", null, {
11151                       onDrag : function(){
11152                           el.sync();  
11153                       }
11154                   });
11155                   dd.setHandleElId(tipTitle.id);
11156                   dd.lock();
11157               }
11158               inited = true;
11159           }
11160           this.enable(); 
11161        },
11162
11163     /**
11164      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11165      * are supported:
11166      * <pre>
11167 Property    Type                   Description
11168 ----------  ---------------------  ------------------------------------------------------------------------
11169 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11170      * </ul>
11171      * @param {Object} config The config object
11172      */
11173        register : function(config){
11174            var cs = config instanceof Array ? config : arguments;
11175            for(var i = 0, len = cs.length; i < len; i++) {
11176                var c = cs[i];
11177                var target = c.target;
11178                if(target){
11179                    if(target instanceof Array){
11180                        for(var j = 0, jlen = target.length; j < jlen; j++){
11181                            tagEls[target[j]] = c;
11182                        }
11183                    }else{
11184                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11185                    }
11186                }
11187            }
11188        },
11189
11190     /**
11191      * Removes this quick tip from its element and destroys it.
11192      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11193      */
11194        unregister : function(el){
11195            delete tagEls[Roo.id(el)];
11196        },
11197
11198     /**
11199      * Enable this quick tip.
11200      */
11201        enable : function(){
11202            if(inited && disabled){
11203                locks.pop();
11204                if(locks.length < 1){
11205                    disabled = false;
11206                }
11207            }
11208        },
11209
11210     /**
11211      * Disable this quick tip.
11212      */
11213        disable : function(){
11214           disabled = true;
11215           clearTimeout(showProc);
11216           clearTimeout(hideProc);
11217           clearTimeout(dismissProc);
11218           if(ce){
11219               hide(true);
11220           }
11221           locks.push(1);
11222        },
11223
11224     /**
11225      * Returns true if the quick tip is enabled, else false.
11226      */
11227        isEnabled : function(){
11228             return !disabled;
11229        },
11230
11231         // private
11232        tagConfig : {
11233            namespace : "roo", // was ext?? this may break..
11234            alt_namespace : "ext",
11235            attribute : "qtip",
11236            width : "width",
11237            target : "target",
11238            title : "qtitle",
11239            hide : "hide",
11240            cls : "qclass"
11241        }
11242    };
11243 }();
11244
11245 // backwards compat
11246 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11247  * Based on:
11248  * Ext JS Library 1.1.1
11249  * Copyright(c) 2006-2007, Ext JS, LLC.
11250  *
11251  * Originally Released Under LGPL - original licence link has changed is not relivant.
11252  *
11253  * Fork - LGPL
11254  * <script type="text/javascript">
11255  */
11256  
11257
11258 /**
11259  * @class Roo.tree.TreePanel
11260  * @extends Roo.data.Tree
11261  * @cfg {Roo.tree.TreeNode} root The root node
11262  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11263  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11264  * @cfg {Boolean} enableDD true to enable drag and drop
11265  * @cfg {Boolean} enableDrag true to enable just drag
11266  * @cfg {Boolean} enableDrop true to enable just drop
11267  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11268  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11269  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11270  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11271  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11272  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11273  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11274  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11275  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11276  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11277  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11278  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11279  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11280  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11281  * @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>
11282  * @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>
11283  * 
11284  * @constructor
11285  * @param {String/HTMLElement/Element} el The container element
11286  * @param {Object} config
11287  */
11288 Roo.tree.TreePanel = function(el, config){
11289     var root = false;
11290     var loader = false;
11291     if (config.root) {
11292         root = config.root;
11293         delete config.root;
11294     }
11295     if (config.loader) {
11296         loader = config.loader;
11297         delete config.loader;
11298     }
11299     
11300     Roo.apply(this, config);
11301     Roo.tree.TreePanel.superclass.constructor.call(this);
11302     this.el = Roo.get(el);
11303     this.el.addClass('x-tree');
11304     //console.log(root);
11305     if (root) {
11306         this.setRootNode( Roo.factory(root, Roo.tree));
11307     }
11308     if (loader) {
11309         this.loader = Roo.factory(loader, Roo.tree);
11310     }
11311    /**
11312     * Read-only. The id of the container element becomes this TreePanel's id.
11313     */
11314     this.id = this.el.id;
11315     this.addEvents({
11316         /**
11317         * @event beforeload
11318         * Fires before a node is loaded, return false to cancel
11319         * @param {Node} node The node being loaded
11320         */
11321         "beforeload" : true,
11322         /**
11323         * @event load
11324         * Fires when a node is loaded
11325         * @param {Node} node The node that was loaded
11326         */
11327         "load" : true,
11328         /**
11329         * @event textchange
11330         * Fires when the text for a node is changed
11331         * @param {Node} node The node
11332         * @param {String} text The new text
11333         * @param {String} oldText The old text
11334         */
11335         "textchange" : true,
11336         /**
11337         * @event beforeexpand
11338         * Fires before a node is expanded, return false to cancel.
11339         * @param {Node} node The node
11340         * @param {Boolean} deep
11341         * @param {Boolean} anim
11342         */
11343         "beforeexpand" : true,
11344         /**
11345         * @event beforecollapse
11346         * Fires before a node is collapsed, return false to cancel.
11347         * @param {Node} node The node
11348         * @param {Boolean} deep
11349         * @param {Boolean} anim
11350         */
11351         "beforecollapse" : true,
11352         /**
11353         * @event expand
11354         * Fires when a node is expanded
11355         * @param {Node} node The node
11356         */
11357         "expand" : true,
11358         /**
11359         * @event disabledchange
11360         * Fires when the disabled status of a node changes
11361         * @param {Node} node The node
11362         * @param {Boolean} disabled
11363         */
11364         "disabledchange" : true,
11365         /**
11366         * @event collapse
11367         * Fires when a node is collapsed
11368         * @param {Node} node The node
11369         */
11370         "collapse" : true,
11371         /**
11372         * @event beforeclick
11373         * Fires before click processing on a node. Return false to cancel the default action.
11374         * @param {Node} node The node
11375         * @param {Roo.EventObject} e The event object
11376         */
11377         "beforeclick":true,
11378         /**
11379         * @event checkchange
11380         * Fires when a node with a checkbox's checked property changes
11381         * @param {Node} this This node
11382         * @param {Boolean} checked
11383         */
11384         "checkchange":true,
11385         /**
11386         * @event click
11387         * Fires when a node is clicked
11388         * @param {Node} node The node
11389         * @param {Roo.EventObject} e The event object
11390         */
11391         "click":true,
11392         /**
11393         * @event dblclick
11394         * Fires when a node is double clicked
11395         * @param {Node} node The node
11396         * @param {Roo.EventObject} e The event object
11397         */
11398         "dblclick":true,
11399         /**
11400         * @event contextmenu
11401         * Fires when a node is right clicked
11402         * @param {Node} node The node
11403         * @param {Roo.EventObject} e The event object
11404         */
11405         "contextmenu":true,
11406         /**
11407         * @event beforechildrenrendered
11408         * Fires right before the child nodes for a node are rendered
11409         * @param {Node} node The node
11410         */
11411         "beforechildrenrendered":true,
11412         /**
11413         * @event startdrag
11414         * Fires when a node starts being dragged
11415         * @param {Roo.tree.TreePanel} this
11416         * @param {Roo.tree.TreeNode} node
11417         * @param {event} e The raw browser event
11418         */ 
11419        "startdrag" : true,
11420        /**
11421         * @event enddrag
11422         * Fires when a drag operation is complete
11423         * @param {Roo.tree.TreePanel} this
11424         * @param {Roo.tree.TreeNode} node
11425         * @param {event} e The raw browser event
11426         */
11427        "enddrag" : true,
11428        /**
11429         * @event dragdrop
11430         * Fires when a dragged node is dropped on a valid DD target
11431         * @param {Roo.tree.TreePanel} this
11432         * @param {Roo.tree.TreeNode} node
11433         * @param {DD} dd The dd it was dropped on
11434         * @param {event} e The raw browser event
11435         */
11436        "dragdrop" : true,
11437        /**
11438         * @event beforenodedrop
11439         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11440         * passed to handlers has the following properties:<br />
11441         * <ul style="padding:5px;padding-left:16px;">
11442         * <li>tree - The TreePanel</li>
11443         * <li>target - The node being targeted for the drop</li>
11444         * <li>data - The drag data from the drag source</li>
11445         * <li>point - The point of the drop - append, above or below</li>
11446         * <li>source - The drag source</li>
11447         * <li>rawEvent - Raw mouse event</li>
11448         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11449         * to be inserted by setting them on this object.</li>
11450         * <li>cancel - Set this to true to cancel the drop.</li>
11451         * </ul>
11452         * @param {Object} dropEvent
11453         */
11454        "beforenodedrop" : true,
11455        /**
11456         * @event nodedrop
11457         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11458         * passed to handlers has the following properties:<br />
11459         * <ul style="padding:5px;padding-left:16px;">
11460         * <li>tree - The TreePanel</li>
11461         * <li>target - The node being targeted for the drop</li>
11462         * <li>data - The drag data from the drag source</li>
11463         * <li>point - The point of the drop - append, above or below</li>
11464         * <li>source - The drag source</li>
11465         * <li>rawEvent - Raw mouse event</li>
11466         * <li>dropNode - Dropped node(s).</li>
11467         * </ul>
11468         * @param {Object} dropEvent
11469         */
11470        "nodedrop" : true,
11471         /**
11472         * @event nodedragover
11473         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11474         * passed to handlers has the following properties:<br />
11475         * <ul style="padding:5px;padding-left:16px;">
11476         * <li>tree - The TreePanel</li>
11477         * <li>target - The node being targeted for the drop</li>
11478         * <li>data - The drag data from the drag source</li>
11479         * <li>point - The point of the drop - append, above or below</li>
11480         * <li>source - The drag source</li>
11481         * <li>rawEvent - Raw mouse event</li>
11482         * <li>dropNode - Drop node(s) provided by the source.</li>
11483         * <li>cancel - Set this to true to signal drop not allowed.</li>
11484         * </ul>
11485         * @param {Object} dragOverEvent
11486         */
11487        "nodedragover" : true,
11488        /**
11489         * @event appendnode
11490         * Fires when append node to the tree
11491         * @param {Roo.tree.TreePanel} this
11492         * @param {Roo.tree.TreeNode} node
11493         * @param {Number} index The index of the newly appended node
11494         */
11495        "appendnode" : true
11496         
11497     });
11498     if(this.singleExpand){
11499        this.on("beforeexpand", this.restrictExpand, this);
11500     }
11501     if (this.editor) {
11502         this.editor.tree = this;
11503         this.editor = Roo.factory(this.editor, Roo.tree);
11504     }
11505     
11506     if (this.selModel) {
11507         this.selModel = Roo.factory(this.selModel, Roo.tree);
11508     }
11509    
11510 };
11511 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11512     rootVisible : true,
11513     animate: Roo.enableFx,
11514     lines : true,
11515     enableDD : false,
11516     hlDrop : Roo.enableFx,
11517   
11518     renderer: false,
11519     
11520     rendererTip: false,
11521     // private
11522     restrictExpand : function(node){
11523         var p = node.parentNode;
11524         if(p){
11525             if(p.expandedChild && p.expandedChild.parentNode == p){
11526                 p.expandedChild.collapse();
11527             }
11528             p.expandedChild = node;
11529         }
11530     },
11531
11532     // private override
11533     setRootNode : function(node){
11534         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11535         if(!this.rootVisible){
11536             node.ui = new Roo.tree.RootTreeNodeUI(node);
11537         }
11538         return node;
11539     },
11540
11541     /**
11542      * Returns the container element for this TreePanel
11543      */
11544     getEl : function(){
11545         return this.el;
11546     },
11547
11548     /**
11549      * Returns the default TreeLoader for this TreePanel
11550      */
11551     getLoader : function(){
11552         return this.loader;
11553     },
11554
11555     /**
11556      * Expand all nodes
11557      */
11558     expandAll : function(){
11559         this.root.expand(true);
11560     },
11561
11562     /**
11563      * Collapse all nodes
11564      */
11565     collapseAll : function(){
11566         this.root.collapse(true);
11567     },
11568
11569     /**
11570      * Returns the selection model used by this TreePanel
11571      */
11572     getSelectionModel : function(){
11573         if(!this.selModel){
11574             this.selModel = new Roo.tree.DefaultSelectionModel();
11575         }
11576         return this.selModel;
11577     },
11578
11579     /**
11580      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11581      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11582      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11583      * @return {Array}
11584      */
11585     getChecked : function(a, startNode){
11586         startNode = startNode || this.root;
11587         var r = [];
11588         var f = function(){
11589             if(this.attributes.checked){
11590                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11591             }
11592         }
11593         startNode.cascade(f);
11594         return r;
11595     },
11596
11597     /**
11598      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11599      * @param {String} path
11600      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11601      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11602      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11603      */
11604     expandPath : function(path, attr, callback){
11605         attr = attr || "id";
11606         var keys = path.split(this.pathSeparator);
11607         var curNode = this.root;
11608         if(curNode.attributes[attr] != keys[1]){ // invalid root
11609             if(callback){
11610                 callback(false, null);
11611             }
11612             return;
11613         }
11614         var index = 1;
11615         var f = function(){
11616             if(++index == keys.length){
11617                 if(callback){
11618                     callback(true, curNode);
11619                 }
11620                 return;
11621             }
11622             var c = curNode.findChild(attr, keys[index]);
11623             if(!c){
11624                 if(callback){
11625                     callback(false, curNode);
11626                 }
11627                 return;
11628             }
11629             curNode = c;
11630             c.expand(false, false, f);
11631         };
11632         curNode.expand(false, false, f);
11633     },
11634
11635     /**
11636      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11637      * @param {String} path
11638      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11639      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11640      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11641      */
11642     selectPath : function(path, attr, callback){
11643         attr = attr || "id";
11644         var keys = path.split(this.pathSeparator);
11645         var v = keys.pop();
11646         if(keys.length > 0){
11647             var f = function(success, node){
11648                 if(success && node){
11649                     var n = node.findChild(attr, v);
11650                     if(n){
11651                         n.select();
11652                         if(callback){
11653                             callback(true, n);
11654                         }
11655                     }else if(callback){
11656                         callback(false, n);
11657                     }
11658                 }else{
11659                     if(callback){
11660                         callback(false, n);
11661                     }
11662                 }
11663             };
11664             this.expandPath(keys.join(this.pathSeparator), attr, f);
11665         }else{
11666             this.root.select();
11667             if(callback){
11668                 callback(true, this.root);
11669             }
11670         }
11671     },
11672
11673     getTreeEl : function(){
11674         return this.el;
11675     },
11676
11677     /**
11678      * Trigger rendering of this TreePanel
11679      */
11680     render : function(){
11681         if (this.innerCt) {
11682             return this; // stop it rendering more than once!!
11683         }
11684         
11685         this.innerCt = this.el.createChild({tag:"ul",
11686                cls:"x-tree-root-ct " +
11687                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11688
11689         if(this.containerScroll){
11690             Roo.dd.ScrollManager.register(this.el);
11691         }
11692         if((this.enableDD || this.enableDrop) && !this.dropZone){
11693            /**
11694             * The dropZone used by this tree if drop is enabled
11695             * @type Roo.tree.TreeDropZone
11696             */
11697              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11698                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11699            });
11700         }
11701         if((this.enableDD || this.enableDrag) && !this.dragZone){
11702            /**
11703             * The dragZone used by this tree if drag is enabled
11704             * @type Roo.tree.TreeDragZone
11705             */
11706             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11707                ddGroup: this.ddGroup || "TreeDD",
11708                scroll: this.ddScroll
11709            });
11710         }
11711         this.getSelectionModel().init(this);
11712         if (!this.root) {
11713             Roo.log("ROOT not set in tree");
11714             return this;
11715         }
11716         this.root.render();
11717         if(!this.rootVisible){
11718             this.root.renderChildren();
11719         }
11720         return this;
11721     }
11722 });/*
11723  * Based on:
11724  * Ext JS Library 1.1.1
11725  * Copyright(c) 2006-2007, Ext JS, LLC.
11726  *
11727  * Originally Released Under LGPL - original licence link has changed is not relivant.
11728  *
11729  * Fork - LGPL
11730  * <script type="text/javascript">
11731  */
11732  
11733
11734 /**
11735  * @class Roo.tree.DefaultSelectionModel
11736  * @extends Roo.util.Observable
11737  * The default single selection for a TreePanel.
11738  * @param {Object} cfg Configuration
11739  */
11740 Roo.tree.DefaultSelectionModel = function(cfg){
11741    this.selNode = null;
11742    
11743    
11744    
11745    this.addEvents({
11746        /**
11747         * @event selectionchange
11748         * Fires when the selected node changes
11749         * @param {DefaultSelectionModel} this
11750         * @param {TreeNode} node the new selection
11751         */
11752        "selectionchange" : true,
11753
11754        /**
11755         * @event beforeselect
11756         * Fires before the selected node changes, return false to cancel the change
11757         * @param {DefaultSelectionModel} this
11758         * @param {TreeNode} node the new selection
11759         * @param {TreeNode} node the old selection
11760         */
11761        "beforeselect" : true
11762    });
11763    
11764     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11765 };
11766
11767 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11768     init : function(tree){
11769         this.tree = tree;
11770         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11771         tree.on("click", this.onNodeClick, this);
11772     },
11773     
11774     onNodeClick : function(node, e){
11775         if (e.ctrlKey && this.selNode == node)  {
11776             this.unselect(node);
11777             return;
11778         }
11779         this.select(node);
11780     },
11781     
11782     /**
11783      * Select a node.
11784      * @param {TreeNode} node The node to select
11785      * @return {TreeNode} The selected node
11786      */
11787     select : function(node){
11788         var last = this.selNode;
11789         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11790             if(last){
11791                 last.ui.onSelectedChange(false);
11792             }
11793             this.selNode = node;
11794             node.ui.onSelectedChange(true);
11795             this.fireEvent("selectionchange", this, node, last);
11796         }
11797         return node;
11798     },
11799     
11800     /**
11801      * Deselect a node.
11802      * @param {TreeNode} node The node to unselect
11803      */
11804     unselect : function(node){
11805         if(this.selNode == node){
11806             this.clearSelections();
11807         }    
11808     },
11809     
11810     /**
11811      * Clear all selections
11812      */
11813     clearSelections : function(){
11814         var n = this.selNode;
11815         if(n){
11816             n.ui.onSelectedChange(false);
11817             this.selNode = null;
11818             this.fireEvent("selectionchange", this, null);
11819         }
11820         return n;
11821     },
11822     
11823     /**
11824      * Get the selected node
11825      * @return {TreeNode} The selected node
11826      */
11827     getSelectedNode : function(){
11828         return this.selNode;    
11829     },
11830     
11831     /**
11832      * Returns true if the node is selected
11833      * @param {TreeNode} node The node to check
11834      * @return {Boolean}
11835      */
11836     isSelected : function(node){
11837         return this.selNode == node;  
11838     },
11839
11840     /**
11841      * Selects the node above the selected node in the tree, intelligently walking the nodes
11842      * @return TreeNode The new selection
11843      */
11844     selectPrevious : function(){
11845         var s = this.selNode || this.lastSelNode;
11846         if(!s){
11847             return null;
11848         }
11849         var ps = s.previousSibling;
11850         if(ps){
11851             if(!ps.isExpanded() || ps.childNodes.length < 1){
11852                 return this.select(ps);
11853             } else{
11854                 var lc = ps.lastChild;
11855                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11856                     lc = lc.lastChild;
11857                 }
11858                 return this.select(lc);
11859             }
11860         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11861             return this.select(s.parentNode);
11862         }
11863         return null;
11864     },
11865
11866     /**
11867      * Selects the node above the selected node in the tree, intelligently walking the nodes
11868      * @return TreeNode The new selection
11869      */
11870     selectNext : function(){
11871         var s = this.selNode || this.lastSelNode;
11872         if(!s){
11873             return null;
11874         }
11875         if(s.firstChild && s.isExpanded()){
11876              return this.select(s.firstChild);
11877          }else if(s.nextSibling){
11878              return this.select(s.nextSibling);
11879          }else if(s.parentNode){
11880             var newS = null;
11881             s.parentNode.bubble(function(){
11882                 if(this.nextSibling){
11883                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11884                     return false;
11885                 }
11886             });
11887             return newS;
11888          }
11889         return null;
11890     },
11891
11892     onKeyDown : function(e){
11893         var s = this.selNode || this.lastSelNode;
11894         // undesirable, but required
11895         var sm = this;
11896         if(!s){
11897             return;
11898         }
11899         var k = e.getKey();
11900         switch(k){
11901              case e.DOWN:
11902                  e.stopEvent();
11903                  this.selectNext();
11904              break;
11905              case e.UP:
11906                  e.stopEvent();
11907                  this.selectPrevious();
11908              break;
11909              case e.RIGHT:
11910                  e.preventDefault();
11911                  if(s.hasChildNodes()){
11912                      if(!s.isExpanded()){
11913                          s.expand();
11914                      }else if(s.firstChild){
11915                          this.select(s.firstChild, e);
11916                      }
11917                  }
11918              break;
11919              case e.LEFT:
11920                  e.preventDefault();
11921                  if(s.hasChildNodes() && s.isExpanded()){
11922                      s.collapse();
11923                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11924                      this.select(s.parentNode, e);
11925                  }
11926              break;
11927         };
11928     }
11929 });
11930
11931 /**
11932  * @class Roo.tree.MultiSelectionModel
11933  * @extends Roo.util.Observable
11934  * Multi selection for a TreePanel.
11935  * @param {Object} cfg Configuration
11936  */
11937 Roo.tree.MultiSelectionModel = function(){
11938    this.selNodes = [];
11939    this.selMap = {};
11940    this.addEvents({
11941        /**
11942         * @event selectionchange
11943         * Fires when the selected nodes change
11944         * @param {MultiSelectionModel} this
11945         * @param {Array} nodes Array of the selected nodes
11946         */
11947        "selectionchange" : true
11948    });
11949    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11950    
11951 };
11952
11953 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11954     init : function(tree){
11955         this.tree = tree;
11956         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11957         tree.on("click", this.onNodeClick, this);
11958     },
11959     
11960     onNodeClick : function(node, e){
11961         this.select(node, e, e.ctrlKey);
11962     },
11963     
11964     /**
11965      * Select a node.
11966      * @param {TreeNode} node The node to select
11967      * @param {EventObject} e (optional) An event associated with the selection
11968      * @param {Boolean} keepExisting True to retain existing selections
11969      * @return {TreeNode} The selected node
11970      */
11971     select : function(node, e, keepExisting){
11972         if(keepExisting !== true){
11973             this.clearSelections(true);
11974         }
11975         if(this.isSelected(node)){
11976             this.lastSelNode = node;
11977             return node;
11978         }
11979         this.selNodes.push(node);
11980         this.selMap[node.id] = node;
11981         this.lastSelNode = node;
11982         node.ui.onSelectedChange(true);
11983         this.fireEvent("selectionchange", this, this.selNodes);
11984         return node;
11985     },
11986     
11987     /**
11988      * Deselect a node.
11989      * @param {TreeNode} node The node to unselect
11990      */
11991     unselect : function(node){
11992         if(this.selMap[node.id]){
11993             node.ui.onSelectedChange(false);
11994             var sn = this.selNodes;
11995             var index = -1;
11996             if(sn.indexOf){
11997                 index = sn.indexOf(node);
11998             }else{
11999                 for(var i = 0, len = sn.length; i < len; i++){
12000                     if(sn[i] == node){
12001                         index = i;
12002                         break;
12003                     }
12004                 }
12005             }
12006             if(index != -1){
12007                 this.selNodes.splice(index, 1);
12008             }
12009             delete this.selMap[node.id];
12010             this.fireEvent("selectionchange", this, this.selNodes);
12011         }
12012     },
12013     
12014     /**
12015      * Clear all selections
12016      */
12017     clearSelections : function(suppressEvent){
12018         var sn = this.selNodes;
12019         if(sn.length > 0){
12020             for(var i = 0, len = sn.length; i < len; i++){
12021                 sn[i].ui.onSelectedChange(false);
12022             }
12023             this.selNodes = [];
12024             this.selMap = {};
12025             if(suppressEvent !== true){
12026                 this.fireEvent("selectionchange", this, this.selNodes);
12027             }
12028         }
12029     },
12030     
12031     /**
12032      * Returns true if the node is selected
12033      * @param {TreeNode} node The node to check
12034      * @return {Boolean}
12035      */
12036     isSelected : function(node){
12037         return this.selMap[node.id] ? true : false;  
12038     },
12039     
12040     /**
12041      * Returns an array of the selected nodes
12042      * @return {Array}
12043      */
12044     getSelectedNodes : function(){
12045         return this.selNodes;    
12046     },
12047
12048     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12049
12050     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12051
12052     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12053 });/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063  
12064 /**
12065  * @class Roo.tree.TreeNode
12066  * @extends Roo.data.Node
12067  * @cfg {String} text The text for this node
12068  * @cfg {Boolean} expanded true to start the node expanded
12069  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12070  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12071  * @cfg {Boolean} disabled true to start the node disabled
12072  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12073  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12074  * @cfg {String} cls A css class to be added to the node
12075  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12076  * @cfg {String} href URL of the link used for the node (defaults to #)
12077  * @cfg {String} hrefTarget target frame for the link
12078  * @cfg {String} qtip An Ext QuickTip for the node
12079  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12080  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12081  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12082  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12083  * (defaults to undefined with no checkbox rendered)
12084  * @constructor
12085  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12086  */
12087 Roo.tree.TreeNode = function(attributes){
12088     attributes = attributes || {};
12089     if(typeof attributes == "string"){
12090         attributes = {text: attributes};
12091     }
12092     this.childrenRendered = false;
12093     this.rendered = false;
12094     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12095     this.expanded = attributes.expanded === true;
12096     this.isTarget = attributes.isTarget !== false;
12097     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12098     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12099
12100     /**
12101      * Read-only. The text for this node. To change it use setText().
12102      * @type String
12103      */
12104     this.text = attributes.text;
12105     /**
12106      * True if this node is disabled.
12107      * @type Boolean
12108      */
12109     this.disabled = attributes.disabled === true;
12110
12111     this.addEvents({
12112         /**
12113         * @event textchange
12114         * Fires when the text for this node is changed
12115         * @param {Node} this This node
12116         * @param {String} text The new text
12117         * @param {String} oldText The old text
12118         */
12119         "textchange" : true,
12120         /**
12121         * @event beforeexpand
12122         * Fires before this node is expanded, return false to cancel.
12123         * @param {Node} this This node
12124         * @param {Boolean} deep
12125         * @param {Boolean} anim
12126         */
12127         "beforeexpand" : true,
12128         /**
12129         * @event beforecollapse
12130         * Fires before this node is collapsed, return false to cancel.
12131         * @param {Node} this This node
12132         * @param {Boolean} deep
12133         * @param {Boolean} anim
12134         */
12135         "beforecollapse" : true,
12136         /**
12137         * @event expand
12138         * Fires when this node is expanded
12139         * @param {Node} this This node
12140         */
12141         "expand" : true,
12142         /**
12143         * @event disabledchange
12144         * Fires when the disabled status of this node changes
12145         * @param {Node} this This node
12146         * @param {Boolean} disabled
12147         */
12148         "disabledchange" : true,
12149         /**
12150         * @event collapse
12151         * Fires when this node is collapsed
12152         * @param {Node} this This node
12153         */
12154         "collapse" : true,
12155         /**
12156         * @event beforeclick
12157         * Fires before click processing. Return false to cancel the default action.
12158         * @param {Node} this This node
12159         * @param {Roo.EventObject} e The event object
12160         */
12161         "beforeclick":true,
12162         /**
12163         * @event checkchange
12164         * Fires when a node with a checkbox's checked property changes
12165         * @param {Node} this This node
12166         * @param {Boolean} checked
12167         */
12168         "checkchange":true,
12169         /**
12170         * @event click
12171         * Fires when this node is clicked
12172         * @param {Node} this This node
12173         * @param {Roo.EventObject} e The event object
12174         */
12175         "click":true,
12176         /**
12177         * @event dblclick
12178         * Fires when this node is double clicked
12179         * @param {Node} this This node
12180         * @param {Roo.EventObject} e The event object
12181         */
12182         "dblclick":true,
12183         /**
12184         * @event contextmenu
12185         * Fires when this node is right clicked
12186         * @param {Node} this This node
12187         * @param {Roo.EventObject} e The event object
12188         */
12189         "contextmenu":true,
12190         /**
12191         * @event beforechildrenrendered
12192         * Fires right before the child nodes for this node are rendered
12193         * @param {Node} this This node
12194         */
12195         "beforechildrenrendered":true
12196     });
12197
12198     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12199
12200     /**
12201      * Read-only. The UI for this node
12202      * @type TreeNodeUI
12203      */
12204     this.ui = new uiClass(this);
12205     
12206     // finally support items[]
12207     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12208         return;
12209     }
12210     
12211     
12212     Roo.each(this.attributes.items, function(c) {
12213         this.appendChild(Roo.factory(c,Roo.Tree));
12214     }, this);
12215     delete this.attributes.items;
12216     
12217     
12218     
12219 };
12220 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12221     preventHScroll: true,
12222     /**
12223      * Returns true if this node is expanded
12224      * @return {Boolean}
12225      */
12226     isExpanded : function(){
12227         return this.expanded;
12228     },
12229
12230     /**
12231      * Returns the UI object for this node
12232      * @return {TreeNodeUI}
12233      */
12234     getUI : function(){
12235         return this.ui;
12236     },
12237
12238     // private override
12239     setFirstChild : function(node){
12240         var of = this.firstChild;
12241         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12242         if(this.childrenRendered && of && node != of){
12243             of.renderIndent(true, true);
12244         }
12245         if(this.rendered){
12246             this.renderIndent(true, true);
12247         }
12248     },
12249
12250     // private override
12251     setLastChild : function(node){
12252         var ol = this.lastChild;
12253         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12254         if(this.childrenRendered && ol && node != ol){
12255             ol.renderIndent(true, true);
12256         }
12257         if(this.rendered){
12258             this.renderIndent(true, true);
12259         }
12260     },
12261
12262     // these methods are overridden to provide lazy rendering support
12263     // private override
12264     appendChild : function()
12265     {
12266         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12267         if(node && this.childrenRendered){
12268             node.render();
12269         }
12270         this.ui.updateExpandIcon();
12271         return node;
12272     },
12273
12274     // private override
12275     removeChild : function(node){
12276         this.ownerTree.getSelectionModel().unselect(node);
12277         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12278         // if it's been rendered remove dom node
12279         if(this.childrenRendered){
12280             node.ui.remove();
12281         }
12282         if(this.childNodes.length < 1){
12283             this.collapse(false, false);
12284         }else{
12285             this.ui.updateExpandIcon();
12286         }
12287         if(!this.firstChild) {
12288             this.childrenRendered = false;
12289         }
12290         return node;
12291     },
12292
12293     // private override
12294     insertBefore : function(node, refNode){
12295         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12296         if(newNode && refNode && this.childrenRendered){
12297             node.render();
12298         }
12299         this.ui.updateExpandIcon();
12300         return newNode;
12301     },
12302
12303     /**
12304      * Sets the text for this node
12305      * @param {String} text
12306      */
12307     setText : function(text){
12308         var oldText = this.text;
12309         this.text = text;
12310         this.attributes.text = text;
12311         if(this.rendered){ // event without subscribing
12312             this.ui.onTextChange(this, text, oldText);
12313         }
12314         this.fireEvent("textchange", this, text, oldText);
12315     },
12316
12317     /**
12318      * Triggers selection of this node
12319      */
12320     select : function(){
12321         this.getOwnerTree().getSelectionModel().select(this);
12322     },
12323
12324     /**
12325      * Triggers deselection of this node
12326      */
12327     unselect : function(){
12328         this.getOwnerTree().getSelectionModel().unselect(this);
12329     },
12330
12331     /**
12332      * Returns true if this node is selected
12333      * @return {Boolean}
12334      */
12335     isSelected : function(){
12336         return this.getOwnerTree().getSelectionModel().isSelected(this);
12337     },
12338
12339     /**
12340      * Expand this node.
12341      * @param {Boolean} deep (optional) True to expand all children as well
12342      * @param {Boolean} anim (optional) false to cancel the default animation
12343      * @param {Function} callback (optional) A callback to be called when
12344      * expanding this node completes (does not wait for deep expand to complete).
12345      * Called with 1 parameter, this node.
12346      */
12347     expand : function(deep, anim, callback){
12348         if(!this.expanded){
12349             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12350                 return;
12351             }
12352             if(!this.childrenRendered){
12353                 this.renderChildren();
12354             }
12355             this.expanded = true;
12356             
12357             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12358                 this.ui.animExpand(function(){
12359                     this.fireEvent("expand", this);
12360                     if(typeof callback == "function"){
12361                         callback(this);
12362                     }
12363                     if(deep === true){
12364                         this.expandChildNodes(true);
12365                     }
12366                 }.createDelegate(this));
12367                 return;
12368             }else{
12369                 this.ui.expand();
12370                 this.fireEvent("expand", this);
12371                 if(typeof callback == "function"){
12372                     callback(this);
12373                 }
12374             }
12375         }else{
12376            if(typeof callback == "function"){
12377                callback(this);
12378            }
12379         }
12380         if(deep === true){
12381             this.expandChildNodes(true);
12382         }
12383     },
12384
12385     isHiddenRoot : function(){
12386         return this.isRoot && !this.getOwnerTree().rootVisible;
12387     },
12388
12389     /**
12390      * Collapse this node.
12391      * @param {Boolean} deep (optional) True to collapse all children as well
12392      * @param {Boolean} anim (optional) false to cancel the default animation
12393      */
12394     collapse : function(deep, anim){
12395         if(this.expanded && !this.isHiddenRoot()){
12396             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12397                 return;
12398             }
12399             this.expanded = false;
12400             if((this.getOwnerTree().animate && anim !== false) || anim){
12401                 this.ui.animCollapse(function(){
12402                     this.fireEvent("collapse", this);
12403                     if(deep === true){
12404                         this.collapseChildNodes(true);
12405                     }
12406                 }.createDelegate(this));
12407                 return;
12408             }else{
12409                 this.ui.collapse();
12410                 this.fireEvent("collapse", this);
12411             }
12412         }
12413         if(deep === true){
12414             var cs = this.childNodes;
12415             for(var i = 0, len = cs.length; i < len; i++) {
12416                 cs[i].collapse(true, false);
12417             }
12418         }
12419     },
12420
12421     // private
12422     delayedExpand : function(delay){
12423         if(!this.expandProcId){
12424             this.expandProcId = this.expand.defer(delay, this);
12425         }
12426     },
12427
12428     // private
12429     cancelExpand : function(){
12430         if(this.expandProcId){
12431             clearTimeout(this.expandProcId);
12432         }
12433         this.expandProcId = false;
12434     },
12435
12436     /**
12437      * Toggles expanded/collapsed state of the node
12438      */
12439     toggle : function(){
12440         if(this.expanded){
12441             this.collapse();
12442         }else{
12443             this.expand();
12444         }
12445     },
12446
12447     /**
12448      * Ensures all parent nodes are expanded
12449      */
12450     ensureVisible : function(callback){
12451         var tree = this.getOwnerTree();
12452         tree.expandPath(this.parentNode.getPath(), false, function(){
12453             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12454             Roo.callback(callback);
12455         }.createDelegate(this));
12456     },
12457
12458     /**
12459      * Expand all child nodes
12460      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12461      */
12462     expandChildNodes : function(deep){
12463         var cs = this.childNodes;
12464         for(var i = 0, len = cs.length; i < len; i++) {
12465                 cs[i].expand(deep);
12466         }
12467     },
12468
12469     /**
12470      * Collapse all child nodes
12471      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12472      */
12473     collapseChildNodes : function(deep){
12474         var cs = this.childNodes;
12475         for(var i = 0, len = cs.length; i < len; i++) {
12476                 cs[i].collapse(deep);
12477         }
12478     },
12479
12480     /**
12481      * Disables this node
12482      */
12483     disable : function(){
12484         this.disabled = true;
12485         this.unselect();
12486         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12487             this.ui.onDisableChange(this, true);
12488         }
12489         this.fireEvent("disabledchange", this, true);
12490     },
12491
12492     /**
12493      * Enables this node
12494      */
12495     enable : function(){
12496         this.disabled = false;
12497         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12498             this.ui.onDisableChange(this, false);
12499         }
12500         this.fireEvent("disabledchange", this, false);
12501     },
12502
12503     // private
12504     renderChildren : function(suppressEvent){
12505         if(suppressEvent !== false){
12506             this.fireEvent("beforechildrenrendered", this);
12507         }
12508         var cs = this.childNodes;
12509         for(var i = 0, len = cs.length; i < len; i++){
12510             cs[i].render(true);
12511         }
12512         this.childrenRendered = true;
12513     },
12514
12515     // private
12516     sort : function(fn, scope){
12517         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12518         if(this.childrenRendered){
12519             var cs = this.childNodes;
12520             for(var i = 0, len = cs.length; i < len; i++){
12521                 cs[i].render(true);
12522             }
12523         }
12524     },
12525
12526     // private
12527     render : function(bulkRender){
12528         this.ui.render(bulkRender);
12529         if(!this.rendered){
12530             this.rendered = true;
12531             if(this.expanded){
12532                 this.expanded = false;
12533                 this.expand(false, false);
12534             }
12535         }
12536     },
12537
12538     // private
12539     renderIndent : function(deep, refresh){
12540         if(refresh){
12541             this.ui.childIndent = null;
12542         }
12543         this.ui.renderIndent();
12544         if(deep === true && this.childrenRendered){
12545             var cs = this.childNodes;
12546             for(var i = 0, len = cs.length; i < len; i++){
12547                 cs[i].renderIndent(true, refresh);
12548             }
12549         }
12550     }
12551 });/*
12552  * Based on:
12553  * Ext JS Library 1.1.1
12554  * Copyright(c) 2006-2007, Ext JS, LLC.
12555  *
12556  * Originally Released Under LGPL - original licence link has changed is not relivant.
12557  *
12558  * Fork - LGPL
12559  * <script type="text/javascript">
12560  */
12561  
12562 /**
12563  * @class Roo.tree.AsyncTreeNode
12564  * @extends Roo.tree.TreeNode
12565  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12566  * @constructor
12567  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12568  */
12569  Roo.tree.AsyncTreeNode = function(config){
12570     this.loaded = false;
12571     this.loading = false;
12572     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12573     /**
12574     * @event beforeload
12575     * Fires before this node is loaded, return false to cancel
12576     * @param {Node} this This node
12577     */
12578     this.addEvents({'beforeload':true, 'load': true});
12579     /**
12580     * @event load
12581     * Fires when this node is loaded
12582     * @param {Node} this This node
12583     */
12584     /**
12585      * The loader used by this node (defaults to using the tree's defined loader)
12586      * @type TreeLoader
12587      * @property loader
12588      */
12589 };
12590 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12591     expand : function(deep, anim, callback){
12592         if(this.loading){ // if an async load is already running, waiting til it's done
12593             var timer;
12594             var f = function(){
12595                 if(!this.loading){ // done loading
12596                     clearInterval(timer);
12597                     this.expand(deep, anim, callback);
12598                 }
12599             }.createDelegate(this);
12600             timer = setInterval(f, 200);
12601             return;
12602         }
12603         if(!this.loaded){
12604             if(this.fireEvent("beforeload", this) === false){
12605                 return;
12606             }
12607             this.loading = true;
12608             this.ui.beforeLoad(this);
12609             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12610             if(loader){
12611                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12612                 return;
12613             }
12614         }
12615         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12616     },
12617     
12618     /**
12619      * Returns true if this node is currently loading
12620      * @return {Boolean}
12621      */
12622     isLoading : function(){
12623         return this.loading;  
12624     },
12625     
12626     loadComplete : function(deep, anim, callback){
12627         this.loading = false;
12628         this.loaded = true;
12629         this.ui.afterLoad(this);
12630         this.fireEvent("load", this);
12631         this.expand(deep, anim, callback);
12632     },
12633     
12634     /**
12635      * Returns true if this node has been loaded
12636      * @return {Boolean}
12637      */
12638     isLoaded : function(){
12639         return this.loaded;
12640     },
12641     
12642     hasChildNodes : function(){
12643         if(!this.isLeaf() && !this.loaded){
12644             return true;
12645         }else{
12646             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12647         }
12648     },
12649
12650     /**
12651      * Trigger a reload for this node
12652      * @param {Function} callback
12653      */
12654     reload : function(callback){
12655         this.collapse(false, false);
12656         while(this.firstChild){
12657             this.removeChild(this.firstChild);
12658         }
12659         this.childrenRendered = false;
12660         this.loaded = false;
12661         if(this.isHiddenRoot()){
12662             this.expanded = false;
12663         }
12664         this.expand(false, false, callback);
12665     }
12666 });/*
12667  * Based on:
12668  * Ext JS Library 1.1.1
12669  * Copyright(c) 2006-2007, Ext JS, LLC.
12670  *
12671  * Originally Released Under LGPL - original licence link has changed is not relivant.
12672  *
12673  * Fork - LGPL
12674  * <script type="text/javascript">
12675  */
12676  
12677 /**
12678  * @class Roo.tree.TreeNodeUI
12679  * @constructor
12680  * @param {Object} node The node to render
12681  * The TreeNode UI implementation is separate from the
12682  * tree implementation. Unless you are customizing the tree UI,
12683  * you should never have to use this directly.
12684  */
12685 Roo.tree.TreeNodeUI = function(node){
12686     this.node = node;
12687     this.rendered = false;
12688     this.animating = false;
12689     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12690 };
12691
12692 Roo.tree.TreeNodeUI.prototype = {
12693     removeChild : function(node){
12694         if(this.rendered){
12695             this.ctNode.removeChild(node.ui.getEl());
12696         }
12697     },
12698
12699     beforeLoad : function(){
12700          this.addClass("x-tree-node-loading");
12701     },
12702
12703     afterLoad : function(){
12704          this.removeClass("x-tree-node-loading");
12705     },
12706
12707     onTextChange : function(node, text, oldText){
12708         if(this.rendered){
12709             this.textNode.innerHTML = text;
12710         }
12711     },
12712
12713     onDisableChange : function(node, state){
12714         this.disabled = state;
12715         if(state){
12716             this.addClass("x-tree-node-disabled");
12717         }else{
12718             this.removeClass("x-tree-node-disabled");
12719         }
12720     },
12721
12722     onSelectedChange : function(state){
12723         if(state){
12724             this.focus();
12725             this.addClass("x-tree-selected");
12726         }else{
12727             //this.blur();
12728             this.removeClass("x-tree-selected");
12729         }
12730     },
12731
12732     onMove : function(tree, node, oldParent, newParent, index, refNode){
12733         this.childIndent = null;
12734         if(this.rendered){
12735             var targetNode = newParent.ui.getContainer();
12736             if(!targetNode){//target not rendered
12737                 this.holder = document.createElement("div");
12738                 this.holder.appendChild(this.wrap);
12739                 return;
12740             }
12741             var insertBefore = refNode ? refNode.ui.getEl() : null;
12742             if(insertBefore){
12743                 targetNode.insertBefore(this.wrap, insertBefore);
12744             }else{
12745                 targetNode.appendChild(this.wrap);
12746             }
12747             this.node.renderIndent(true);
12748         }
12749     },
12750
12751     addClass : function(cls){
12752         if(this.elNode){
12753             Roo.fly(this.elNode).addClass(cls);
12754         }
12755     },
12756
12757     removeClass : function(cls){
12758         if(this.elNode){
12759             Roo.fly(this.elNode).removeClass(cls);
12760         }
12761     },
12762
12763     remove : function(){
12764         if(this.rendered){
12765             this.holder = document.createElement("div");
12766             this.holder.appendChild(this.wrap);
12767         }
12768     },
12769
12770     fireEvent : function(){
12771         return this.node.fireEvent.apply(this.node, arguments);
12772     },
12773
12774     initEvents : function(){
12775         this.node.on("move", this.onMove, this);
12776         var E = Roo.EventManager;
12777         var a = this.anchor;
12778
12779         var el = Roo.fly(a, '_treeui');
12780
12781         if(Roo.isOpera){ // opera render bug ignores the CSS
12782             el.setStyle("text-decoration", "none");
12783         }
12784
12785         el.on("click", this.onClick, this);
12786         el.on("dblclick", this.onDblClick, this);
12787
12788         if(this.checkbox){
12789             Roo.EventManager.on(this.checkbox,
12790                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12791         }
12792
12793         el.on("contextmenu", this.onContextMenu, this);
12794
12795         var icon = Roo.fly(this.iconNode);
12796         icon.on("click", this.onClick, this);
12797         icon.on("dblclick", this.onDblClick, this);
12798         icon.on("contextmenu", this.onContextMenu, this);
12799         E.on(this.ecNode, "click", this.ecClick, this, true);
12800
12801         if(this.node.disabled){
12802             this.addClass("x-tree-node-disabled");
12803         }
12804         if(this.node.hidden){
12805             this.addClass("x-tree-node-disabled");
12806         }
12807         var ot = this.node.getOwnerTree();
12808         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12809         if(dd && (!this.node.isRoot || ot.rootVisible)){
12810             Roo.dd.Registry.register(this.elNode, {
12811                 node: this.node,
12812                 handles: this.getDDHandles(),
12813                 isHandle: false
12814             });
12815         }
12816     },
12817
12818     getDDHandles : function(){
12819         return [this.iconNode, this.textNode];
12820     },
12821
12822     hide : function(){
12823         if(this.rendered){
12824             this.wrap.style.display = "none";
12825         }
12826     },
12827
12828     show : function(){
12829         if(this.rendered){
12830             this.wrap.style.display = "";
12831         }
12832     },
12833
12834     onContextMenu : function(e){
12835         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12836             e.preventDefault();
12837             this.focus();
12838             this.fireEvent("contextmenu", this.node, e);
12839         }
12840     },
12841
12842     onClick : function(e){
12843         if(this.dropping){
12844             e.stopEvent();
12845             return;
12846         }
12847         if(this.fireEvent("beforeclick", this.node, e) !== false){
12848             if(!this.disabled && this.node.attributes.href){
12849                 this.fireEvent("click", this.node, e);
12850                 return;
12851             }
12852             e.preventDefault();
12853             if(this.disabled){
12854                 return;
12855             }
12856
12857             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12858                 this.node.toggle();
12859             }
12860
12861             this.fireEvent("click", this.node, e);
12862         }else{
12863             e.stopEvent();
12864         }
12865     },
12866
12867     onDblClick : function(e){
12868         e.preventDefault();
12869         if(this.disabled){
12870             return;
12871         }
12872         if(this.checkbox){
12873             this.toggleCheck();
12874         }
12875         if(!this.animating && this.node.hasChildNodes()){
12876             this.node.toggle();
12877         }
12878         this.fireEvent("dblclick", this.node, e);
12879     },
12880
12881     onCheckChange : function(){
12882         var checked = this.checkbox.checked;
12883         this.node.attributes.checked = checked;
12884         this.fireEvent('checkchange', this.node, checked);
12885     },
12886
12887     ecClick : function(e){
12888         if(!this.animating && this.node.hasChildNodes()){
12889             this.node.toggle();
12890         }
12891     },
12892
12893     startDrop : function(){
12894         this.dropping = true;
12895     },
12896
12897     // delayed drop so the click event doesn't get fired on a drop
12898     endDrop : function(){
12899        setTimeout(function(){
12900            this.dropping = false;
12901        }.createDelegate(this), 50);
12902     },
12903
12904     expand : function(){
12905         this.updateExpandIcon();
12906         this.ctNode.style.display = "";
12907     },
12908
12909     focus : function(){
12910         if(!this.node.preventHScroll){
12911             try{this.anchor.focus();
12912             }catch(e){}
12913         }else if(!Roo.isIE){
12914             try{
12915                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12916                 var l = noscroll.scrollLeft;
12917                 this.anchor.focus();
12918                 noscroll.scrollLeft = l;
12919             }catch(e){}
12920         }
12921     },
12922
12923     toggleCheck : function(value){
12924         var cb = this.checkbox;
12925         if(cb){
12926             cb.checked = (value === undefined ? !cb.checked : value);
12927         }
12928     },
12929
12930     blur : function(){
12931         try{
12932             this.anchor.blur();
12933         }catch(e){}
12934     },
12935
12936     animExpand : function(callback){
12937         var ct = Roo.get(this.ctNode);
12938         ct.stopFx();
12939         if(!this.node.hasChildNodes()){
12940             this.updateExpandIcon();
12941             this.ctNode.style.display = "";
12942             Roo.callback(callback);
12943             return;
12944         }
12945         this.animating = true;
12946         this.updateExpandIcon();
12947
12948         ct.slideIn('t', {
12949            callback : function(){
12950                this.animating = false;
12951                Roo.callback(callback);
12952             },
12953             scope: this,
12954             duration: this.node.ownerTree.duration || .25
12955         });
12956     },
12957
12958     highlight : function(){
12959         var tree = this.node.getOwnerTree();
12960         Roo.fly(this.wrap).highlight(
12961             tree.hlColor || "C3DAF9",
12962             {endColor: tree.hlBaseColor}
12963         );
12964     },
12965
12966     collapse : function(){
12967         this.updateExpandIcon();
12968         this.ctNode.style.display = "none";
12969     },
12970
12971     animCollapse : function(callback){
12972         var ct = Roo.get(this.ctNode);
12973         ct.enableDisplayMode('block');
12974         ct.stopFx();
12975
12976         this.animating = true;
12977         this.updateExpandIcon();
12978
12979         ct.slideOut('t', {
12980             callback : function(){
12981                this.animating = false;
12982                Roo.callback(callback);
12983             },
12984             scope: this,
12985             duration: this.node.ownerTree.duration || .25
12986         });
12987     },
12988
12989     getContainer : function(){
12990         return this.ctNode;
12991     },
12992
12993     getEl : function(){
12994         return this.wrap;
12995     },
12996
12997     appendDDGhost : function(ghostNode){
12998         ghostNode.appendChild(this.elNode.cloneNode(true));
12999     },
13000
13001     getDDRepairXY : function(){
13002         return Roo.lib.Dom.getXY(this.iconNode);
13003     },
13004
13005     onRender : function(){
13006         this.render();
13007     },
13008
13009     render : function(bulkRender){
13010         var n = this.node, a = n.attributes;
13011         var targetNode = n.parentNode ?
13012               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13013
13014         if(!this.rendered){
13015             this.rendered = true;
13016
13017             this.renderElements(n, a, targetNode, bulkRender);
13018
13019             if(a.qtip){
13020                if(this.textNode.setAttributeNS){
13021                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13022                    if(a.qtipTitle){
13023                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13024                    }
13025                }else{
13026                    this.textNode.setAttribute("ext:qtip", a.qtip);
13027                    if(a.qtipTitle){
13028                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13029                    }
13030                }
13031             }else if(a.qtipCfg){
13032                 a.qtipCfg.target = Roo.id(this.textNode);
13033                 Roo.QuickTips.register(a.qtipCfg);
13034             }
13035             this.initEvents();
13036             if(!this.node.expanded){
13037                 this.updateExpandIcon();
13038             }
13039         }else{
13040             if(bulkRender === true) {
13041                 targetNode.appendChild(this.wrap);
13042             }
13043         }
13044     },
13045
13046     renderElements : function(n, a, targetNode, bulkRender)
13047     {
13048         // add some indent caching, this helps performance when rendering a large tree
13049         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13050         var t = n.getOwnerTree();
13051         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13052         if (typeof(n.attributes.html) != 'undefined') {
13053             txt = n.attributes.html;
13054         }
13055         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13056         var cb = typeof a.checked == 'boolean';
13057         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13058         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13059             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13060             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13061             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13062             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13063             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13064              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13065                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13066             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13067             "</li>"];
13068
13069         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13070             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13071                                 n.nextSibling.ui.getEl(), buf.join(""));
13072         }else{
13073             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13074         }
13075
13076         this.elNode = this.wrap.childNodes[0];
13077         this.ctNode = this.wrap.childNodes[1];
13078         var cs = this.elNode.childNodes;
13079         this.indentNode = cs[0];
13080         this.ecNode = cs[1];
13081         this.iconNode = cs[2];
13082         var index = 3;
13083         if(cb){
13084             this.checkbox = cs[3];
13085             index++;
13086         }
13087         this.anchor = cs[index];
13088         this.textNode = cs[index].firstChild;
13089     },
13090
13091     getAnchor : function(){
13092         return this.anchor;
13093     },
13094
13095     getTextEl : function(){
13096         return this.textNode;
13097     },
13098
13099     getIconEl : function(){
13100         return this.iconNode;
13101     },
13102
13103     isChecked : function(){
13104         return this.checkbox ? this.checkbox.checked : false;
13105     },
13106
13107     updateExpandIcon : function(){
13108         if(this.rendered){
13109             var n = this.node, c1, c2;
13110             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13111             var hasChild = n.hasChildNodes();
13112             if(hasChild){
13113                 if(n.expanded){
13114                     cls += "-minus";
13115                     c1 = "x-tree-node-collapsed";
13116                     c2 = "x-tree-node-expanded";
13117                 }else{
13118                     cls += "-plus";
13119                     c1 = "x-tree-node-expanded";
13120                     c2 = "x-tree-node-collapsed";
13121                 }
13122                 if(this.wasLeaf){
13123                     this.removeClass("x-tree-node-leaf");
13124                     this.wasLeaf = false;
13125                 }
13126                 if(this.c1 != c1 || this.c2 != c2){
13127                     Roo.fly(this.elNode).replaceClass(c1, c2);
13128                     this.c1 = c1; this.c2 = c2;
13129                 }
13130             }else{
13131                 // this changes non-leafs into leafs if they have no children.
13132                 // it's not very rational behaviour..
13133                 
13134                 if(!this.wasLeaf && this.node.leaf){
13135                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13136                     delete this.c1;
13137                     delete this.c2;
13138                     this.wasLeaf = true;
13139                 }
13140             }
13141             var ecc = "x-tree-ec-icon "+cls;
13142             if(this.ecc != ecc){
13143                 this.ecNode.className = ecc;
13144                 this.ecc = ecc;
13145             }
13146         }
13147     },
13148
13149     getChildIndent : function(){
13150         if(!this.childIndent){
13151             var buf = [];
13152             var p = this.node;
13153             while(p){
13154                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13155                     if(!p.isLast()) {
13156                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13157                     } else {
13158                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13159                     }
13160                 }
13161                 p = p.parentNode;
13162             }
13163             this.childIndent = buf.join("");
13164         }
13165         return this.childIndent;
13166     },
13167
13168     renderIndent : function(){
13169         if(this.rendered){
13170             var indent = "";
13171             var p = this.node.parentNode;
13172             if(p){
13173                 indent = p.ui.getChildIndent();
13174             }
13175             if(this.indentMarkup != indent){ // don't rerender if not required
13176                 this.indentNode.innerHTML = indent;
13177                 this.indentMarkup = indent;
13178             }
13179             this.updateExpandIcon();
13180         }
13181     }
13182 };
13183
13184 Roo.tree.RootTreeNodeUI = function(){
13185     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13186 };
13187 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13188     render : function(){
13189         if(!this.rendered){
13190             var targetNode = this.node.ownerTree.innerCt.dom;
13191             this.node.expanded = true;
13192             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13193             this.wrap = this.ctNode = targetNode.firstChild;
13194         }
13195     },
13196     collapse : function(){
13197     },
13198     expand : function(){
13199     }
13200 });/*
13201  * Based on:
13202  * Ext JS Library 1.1.1
13203  * Copyright(c) 2006-2007, Ext JS, LLC.
13204  *
13205  * Originally Released Under LGPL - original licence link has changed is not relivant.
13206  *
13207  * Fork - LGPL
13208  * <script type="text/javascript">
13209  */
13210 /**
13211  * @class Roo.tree.TreeLoader
13212  * @extends Roo.util.Observable
13213  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13214  * nodes from a specified URL. The response must be a javascript Array definition
13215  * who's elements are node definition objects. eg:
13216  * <pre><code>
13217 {  success : true,
13218    data :      [
13219    
13220     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13221     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13222     ]
13223 }
13224
13225
13226 </code></pre>
13227  * <br><br>
13228  * The old style respose with just an array is still supported, but not recommended.
13229  * <br><br>
13230  *
13231  * A server request is sent, and child nodes are loaded only when a node is expanded.
13232  * The loading node's id is passed to the server under the parameter name "node" to
13233  * enable the server to produce the correct child nodes.
13234  * <br><br>
13235  * To pass extra parameters, an event handler may be attached to the "beforeload"
13236  * event, and the parameters specified in the TreeLoader's baseParams property:
13237  * <pre><code>
13238     myTreeLoader.on("beforeload", function(treeLoader, node) {
13239         this.baseParams.category = node.attributes.category;
13240     }, this);
13241     
13242 </code></pre>
13243  *
13244  * This would pass an HTTP parameter called "category" to the server containing
13245  * the value of the Node's "category" attribute.
13246  * @constructor
13247  * Creates a new Treeloader.
13248  * @param {Object} config A config object containing config properties.
13249  */
13250 Roo.tree.TreeLoader = function(config){
13251     this.baseParams = {};
13252     this.requestMethod = "POST";
13253     Roo.apply(this, config);
13254
13255     this.addEvents({
13256     
13257         /**
13258          * @event beforeload
13259          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13260          * @param {Object} This TreeLoader object.
13261          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13262          * @param {Object} callback The callback function specified in the {@link #load} call.
13263          */
13264         beforeload : true,
13265         /**
13266          * @event load
13267          * Fires when the node has been successfuly loaded.
13268          * @param {Object} This TreeLoader object.
13269          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13270          * @param {Object} response The response object containing the data from the server.
13271          */
13272         load : true,
13273         /**
13274          * @event loadexception
13275          * Fires if the network request failed.
13276          * @param {Object} This TreeLoader object.
13277          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13278          * @param {Object} response The response object containing the data from the server.
13279          */
13280         loadexception : true,
13281         /**
13282          * @event create
13283          * Fires before a node is created, enabling you to return custom Node types 
13284          * @param {Object} This TreeLoader object.
13285          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13286          */
13287         create : true
13288     });
13289
13290     Roo.tree.TreeLoader.superclass.constructor.call(this);
13291 };
13292
13293 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13294     /**
13295     * @cfg {String} dataUrl The URL from which to request a Json string which
13296     * specifies an array of node definition object representing the child nodes
13297     * to be loaded.
13298     */
13299     /**
13300     * @cfg {String} requestMethod either GET or POST
13301     * defaults to POST (due to BC)
13302     * to be loaded.
13303     */
13304     /**
13305     * @cfg {Object} baseParams (optional) An object containing properties which
13306     * specify HTTP parameters to be passed to each request for child nodes.
13307     */
13308     /**
13309     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13310     * created by this loader. If the attributes sent by the server have an attribute in this object,
13311     * they take priority.
13312     */
13313     /**
13314     * @cfg {Object} uiProviders (optional) An object containing properties which
13315     * 
13316     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13317     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13318     * <i>uiProvider</i> attribute of a returned child node is a string rather
13319     * than a reference to a TreeNodeUI implementation, this that string value
13320     * is used as a property name in the uiProviders object. You can define the provider named
13321     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13322     */
13323     uiProviders : {},
13324
13325     /**
13326     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13327     * child nodes before loading.
13328     */
13329     clearOnLoad : true,
13330
13331     /**
13332     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13333     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13334     * Grid query { data : [ .....] }
13335     */
13336     
13337     root : false,
13338      /**
13339     * @cfg {String} queryParam (optional) 
13340     * Name of the query as it will be passed on the querystring (defaults to 'node')
13341     * eg. the request will be ?node=[id]
13342     */
13343     
13344     
13345     queryParam: false,
13346     
13347     /**
13348      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13349      * This is called automatically when a node is expanded, but may be used to reload
13350      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13351      * @param {Roo.tree.TreeNode} node
13352      * @param {Function} callback
13353      */
13354     load : function(node, callback){
13355         if(this.clearOnLoad){
13356             while(node.firstChild){
13357                 node.removeChild(node.firstChild);
13358             }
13359         }
13360         if(node.attributes.children){ // preloaded json children
13361             var cs = node.attributes.children;
13362             for(var i = 0, len = cs.length; i < len; i++){
13363                 node.appendChild(this.createNode(cs[i]));
13364             }
13365             if(typeof callback == "function"){
13366                 callback();
13367             }
13368         }else if(this.dataUrl){
13369             this.requestData(node, callback);
13370         }
13371     },
13372
13373     getParams: function(node){
13374         var buf = [], bp = this.baseParams;
13375         for(var key in bp){
13376             if(typeof bp[key] != "function"){
13377                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13378             }
13379         }
13380         var n = this.queryParam === false ? 'node' : this.queryParam;
13381         buf.push(n + "=", encodeURIComponent(node.id));
13382         return buf.join("");
13383     },
13384
13385     requestData : function(node, callback){
13386         if(this.fireEvent("beforeload", this, node, callback) !== false){
13387             this.transId = Roo.Ajax.request({
13388                 method:this.requestMethod,
13389                 url: this.dataUrl||this.url,
13390                 success: this.handleResponse,
13391                 failure: this.handleFailure,
13392                 scope: this,
13393                 argument: {callback: callback, node: node},
13394                 params: this.getParams(node)
13395             });
13396         }else{
13397             // if the load is cancelled, make sure we notify
13398             // the node that we are done
13399             if(typeof callback == "function"){
13400                 callback();
13401             }
13402         }
13403     },
13404
13405     isLoading : function(){
13406         return this.transId ? true : false;
13407     },
13408
13409     abort : function(){
13410         if(this.isLoading()){
13411             Roo.Ajax.abort(this.transId);
13412         }
13413     },
13414
13415     // private
13416     createNode : function(attr)
13417     {
13418         // apply baseAttrs, nice idea Corey!
13419         if(this.baseAttrs){
13420             Roo.applyIf(attr, this.baseAttrs);
13421         }
13422         if(this.applyLoader !== false){
13423             attr.loader = this;
13424         }
13425         // uiProvider = depreciated..
13426         
13427         if(typeof(attr.uiProvider) == 'string'){
13428            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13429                 /**  eval:var:attr */ eval(attr.uiProvider);
13430         }
13431         if(typeof(this.uiProviders['default']) != 'undefined') {
13432             attr.uiProvider = this.uiProviders['default'];
13433         }
13434         
13435         this.fireEvent('create', this, attr);
13436         
13437         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13438         return(attr.leaf ?
13439                         new Roo.tree.TreeNode(attr) :
13440                         new Roo.tree.AsyncTreeNode(attr));
13441     },
13442
13443     processResponse : function(response, node, callback)
13444     {
13445         var json = response.responseText;
13446         try {
13447             
13448             var o = Roo.decode(json);
13449             
13450             if (this.root === false && typeof(o.success) != undefined) {
13451                 this.root = 'data'; // the default behaviour for list like data..
13452                 }
13453                 
13454             if (this.root !== false &&  !o.success) {
13455                 // it's a failure condition.
13456                 var a = response.argument;
13457                 this.fireEvent("loadexception", this, a.node, response);
13458                 Roo.log("Load failed - should have a handler really");
13459                 return;
13460             }
13461             
13462             
13463             
13464             if (this.root !== false) {
13465                  o = o[this.root];
13466             }
13467             
13468             for(var i = 0, len = o.length; i < len; i++){
13469                 var n = this.createNode(o[i]);
13470                 if(n){
13471                     node.appendChild(n);
13472                 }
13473             }
13474             if(typeof callback == "function"){
13475                 callback(this, node);
13476             }
13477         }catch(e){
13478             this.handleFailure(response);
13479         }
13480     },
13481
13482     handleResponse : function(response){
13483         this.transId = false;
13484         var a = response.argument;
13485         this.processResponse(response, a.node, a.callback);
13486         this.fireEvent("load", this, a.node, response);
13487     },
13488
13489     handleFailure : function(response)
13490     {
13491         // should handle failure better..
13492         this.transId = false;
13493         var a = response.argument;
13494         this.fireEvent("loadexception", this, a.node, response);
13495         if(typeof a.callback == "function"){
13496             a.callback(this, a.node);
13497         }
13498     }
13499 });/*
13500  * Based on:
13501  * Ext JS Library 1.1.1
13502  * Copyright(c) 2006-2007, Ext JS, LLC.
13503  *
13504  * Originally Released Under LGPL - original licence link has changed is not relivant.
13505  *
13506  * Fork - LGPL
13507  * <script type="text/javascript">
13508  */
13509
13510 /**
13511 * @class Roo.tree.TreeFilter
13512 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13513 * @param {TreePanel} tree
13514 * @param {Object} config (optional)
13515  */
13516 Roo.tree.TreeFilter = function(tree, config){
13517     this.tree = tree;
13518     this.filtered = {};
13519     Roo.apply(this, config);
13520 };
13521
13522 Roo.tree.TreeFilter.prototype = {
13523     clearBlank:false,
13524     reverse:false,
13525     autoClear:false,
13526     remove:false,
13527
13528      /**
13529      * Filter the data by a specific attribute.
13530      * @param {String/RegExp} value Either string that the attribute value
13531      * should start with or a RegExp to test against the attribute
13532      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13533      * @param {TreeNode} startNode (optional) The node to start the filter at.
13534      */
13535     filter : function(value, attr, startNode){
13536         attr = attr || "text";
13537         var f;
13538         if(typeof value == "string"){
13539             var vlen = value.length;
13540             // auto clear empty filter
13541             if(vlen == 0 && this.clearBlank){
13542                 this.clear();
13543                 return;
13544             }
13545             value = value.toLowerCase();
13546             f = function(n){
13547                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13548             };
13549         }else if(value.exec){ // regex?
13550             f = function(n){
13551                 return value.test(n.attributes[attr]);
13552             };
13553         }else{
13554             throw 'Illegal filter type, must be string or regex';
13555         }
13556         this.filterBy(f, null, startNode);
13557         },
13558
13559     /**
13560      * Filter by a function. The passed function will be called with each
13561      * node in the tree (or from the startNode). If the function returns true, the node is kept
13562      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13563      * @param {Function} fn The filter function
13564      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13565      */
13566     filterBy : function(fn, scope, startNode){
13567         startNode = startNode || this.tree.root;
13568         if(this.autoClear){
13569             this.clear();
13570         }
13571         var af = this.filtered, rv = this.reverse;
13572         var f = function(n){
13573             if(n == startNode){
13574                 return true;
13575             }
13576             if(af[n.id]){
13577                 return false;
13578             }
13579             var m = fn.call(scope || n, n);
13580             if(!m || rv){
13581                 af[n.id] = n;
13582                 n.ui.hide();
13583                 return false;
13584             }
13585             return true;
13586         };
13587         startNode.cascade(f);
13588         if(this.remove){
13589            for(var id in af){
13590                if(typeof id != "function"){
13591                    var n = af[id];
13592                    if(n && n.parentNode){
13593                        n.parentNode.removeChild(n);
13594                    }
13595                }
13596            }
13597         }
13598     },
13599
13600     /**
13601      * Clears the current filter. Note: with the "remove" option
13602      * set a filter cannot be cleared.
13603      */
13604     clear : function(){
13605         var t = this.tree;
13606         var af = this.filtered;
13607         for(var id in af){
13608             if(typeof id != "function"){
13609                 var n = af[id];
13610                 if(n){
13611                     n.ui.show();
13612                 }
13613             }
13614         }
13615         this.filtered = {};
13616     }
13617 };
13618 /*
13619  * Based on:
13620  * Ext JS Library 1.1.1
13621  * Copyright(c) 2006-2007, Ext JS, LLC.
13622  *
13623  * Originally Released Under LGPL - original licence link has changed is not relivant.
13624  *
13625  * Fork - LGPL
13626  * <script type="text/javascript">
13627  */
13628  
13629
13630 /**
13631  * @class Roo.tree.TreeSorter
13632  * Provides sorting of nodes in a TreePanel
13633  * 
13634  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13635  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13636  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13637  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13638  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13639  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13640  * @constructor
13641  * @param {TreePanel} tree
13642  * @param {Object} config
13643  */
13644 Roo.tree.TreeSorter = function(tree, config){
13645     Roo.apply(this, config);
13646     tree.on("beforechildrenrendered", this.doSort, this);
13647     tree.on("append", this.updateSort, this);
13648     tree.on("insert", this.updateSort, this);
13649     
13650     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13651     var p = this.property || "text";
13652     var sortType = this.sortType;
13653     var fs = this.folderSort;
13654     var cs = this.caseSensitive === true;
13655     var leafAttr = this.leafAttr || 'leaf';
13656
13657     this.sortFn = function(n1, n2){
13658         if(fs){
13659             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13660                 return 1;
13661             }
13662             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13663                 return -1;
13664             }
13665         }
13666         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13667         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13668         if(v1 < v2){
13669                         return dsc ? +1 : -1;
13670                 }else if(v1 > v2){
13671                         return dsc ? -1 : +1;
13672         }else{
13673                 return 0;
13674         }
13675     };
13676 };
13677
13678 Roo.tree.TreeSorter.prototype = {
13679     doSort : function(node){
13680         node.sort(this.sortFn);
13681     },
13682     
13683     compareNodes : function(n1, n2){
13684         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13685     },
13686     
13687     updateSort : function(tree, node){
13688         if(node.childrenRendered){
13689             this.doSort.defer(1, this, [node]);
13690         }
13691     }
13692 };/*
13693  * Based on:
13694  * Ext JS Library 1.1.1
13695  * Copyright(c) 2006-2007, Ext JS, LLC.
13696  *
13697  * Originally Released Under LGPL - original licence link has changed is not relivant.
13698  *
13699  * Fork - LGPL
13700  * <script type="text/javascript">
13701  */
13702
13703 if(Roo.dd.DropZone){
13704     
13705 Roo.tree.TreeDropZone = function(tree, config){
13706     this.allowParentInsert = false;
13707     this.allowContainerDrop = false;
13708     this.appendOnly = false;
13709     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13710     this.tree = tree;
13711     this.lastInsertClass = "x-tree-no-status";
13712     this.dragOverData = {};
13713 };
13714
13715 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13716     ddGroup : "TreeDD",
13717     scroll:  true,
13718     
13719     expandDelay : 1000,
13720     
13721     expandNode : function(node){
13722         if(node.hasChildNodes() && !node.isExpanded()){
13723             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13724         }
13725     },
13726     
13727     queueExpand : function(node){
13728         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13729     },
13730     
13731     cancelExpand : function(){
13732         if(this.expandProcId){
13733             clearTimeout(this.expandProcId);
13734             this.expandProcId = false;
13735         }
13736     },
13737     
13738     isValidDropPoint : function(n, pt, dd, e, data){
13739         if(!n || !data){ return false; }
13740         var targetNode = n.node;
13741         var dropNode = data.node;
13742         // default drop rules
13743         if(!(targetNode && targetNode.isTarget && pt)){
13744             return false;
13745         }
13746         if(pt == "append" && targetNode.allowChildren === false){
13747             return false;
13748         }
13749         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13750             return false;
13751         }
13752         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13753             return false;
13754         }
13755         // reuse the object
13756         var overEvent = this.dragOverData;
13757         overEvent.tree = this.tree;
13758         overEvent.target = targetNode;
13759         overEvent.data = data;
13760         overEvent.point = pt;
13761         overEvent.source = dd;
13762         overEvent.rawEvent = e;
13763         overEvent.dropNode = dropNode;
13764         overEvent.cancel = false;  
13765         var result = this.tree.fireEvent("nodedragover", overEvent);
13766         return overEvent.cancel === false && result !== false;
13767     },
13768     
13769     getDropPoint : function(e, n, dd)
13770     {
13771         var tn = n.node;
13772         if(tn.isRoot){
13773             return tn.allowChildren !== false ? "append" : false; // always append for root
13774         }
13775         var dragEl = n.ddel;
13776         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13777         var y = Roo.lib.Event.getPageY(e);
13778         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13779         
13780         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13781         var noAppend = tn.allowChildren === false;
13782         if(this.appendOnly || tn.parentNode.allowChildren === false){
13783             return noAppend ? false : "append";
13784         }
13785         var noBelow = false;
13786         if(!this.allowParentInsert){
13787             noBelow = tn.hasChildNodes() && tn.isExpanded();
13788         }
13789         var q = (b - t) / (noAppend ? 2 : 3);
13790         if(y >= t && y < (t + q)){
13791             return "above";
13792         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13793             return "below";
13794         }else{
13795             return "append";
13796         }
13797     },
13798     
13799     onNodeEnter : function(n, dd, e, data)
13800     {
13801         this.cancelExpand();
13802     },
13803     
13804     onNodeOver : function(n, dd, e, data)
13805     {
13806        
13807         var pt = this.getDropPoint(e, n, dd);
13808         var node = n.node;
13809         
13810         // auto node expand check
13811         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13812             this.queueExpand(node);
13813         }else if(pt != "append"){
13814             this.cancelExpand();
13815         }
13816         
13817         // set the insert point style on the target node
13818         var returnCls = this.dropNotAllowed;
13819         if(this.isValidDropPoint(n, pt, dd, e, data)){
13820            if(pt){
13821                var el = n.ddel;
13822                var cls;
13823                if(pt == "above"){
13824                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13825                    cls = "x-tree-drag-insert-above";
13826                }else if(pt == "below"){
13827                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13828                    cls = "x-tree-drag-insert-below";
13829                }else{
13830                    returnCls = "x-tree-drop-ok-append";
13831                    cls = "x-tree-drag-append";
13832                }
13833                if(this.lastInsertClass != cls){
13834                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13835                    this.lastInsertClass = cls;
13836                }
13837            }
13838        }
13839        return returnCls;
13840     },
13841     
13842     onNodeOut : function(n, dd, e, data){
13843         
13844         this.cancelExpand();
13845         this.removeDropIndicators(n);
13846     },
13847     
13848     onNodeDrop : function(n, dd, e, data){
13849         var point = this.getDropPoint(e, n, dd);
13850         var targetNode = n.node;
13851         targetNode.ui.startDrop();
13852         if(!this.isValidDropPoint(n, point, dd, e, data)){
13853             targetNode.ui.endDrop();
13854             return false;
13855         }
13856         // first try to find the drop node
13857         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13858         var dropEvent = {
13859             tree : this.tree,
13860             target: targetNode,
13861             data: data,
13862             point: point,
13863             source: dd,
13864             rawEvent: e,
13865             dropNode: dropNode,
13866             cancel: !dropNode   
13867         };
13868         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13869         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13870             targetNode.ui.endDrop();
13871             return false;
13872         }
13873         // allow target changing
13874         targetNode = dropEvent.target;
13875         if(point == "append" && !targetNode.isExpanded()){
13876             targetNode.expand(false, null, function(){
13877                 this.completeDrop(dropEvent);
13878             }.createDelegate(this));
13879         }else{
13880             this.completeDrop(dropEvent);
13881         }
13882         return true;
13883     },
13884     
13885     completeDrop : function(de){
13886         var ns = de.dropNode, p = de.point, t = de.target;
13887         if(!(ns instanceof Array)){
13888             ns = [ns];
13889         }
13890         var n;
13891         for(var i = 0, len = ns.length; i < len; i++){
13892             n = ns[i];
13893             if(p == "above"){
13894                 t.parentNode.insertBefore(n, t);
13895             }else if(p == "below"){
13896                 t.parentNode.insertBefore(n, t.nextSibling);
13897             }else{
13898                 t.appendChild(n);
13899             }
13900         }
13901         n.ui.focus();
13902         if(this.tree.hlDrop){
13903             n.ui.highlight();
13904         }
13905         t.ui.endDrop();
13906         this.tree.fireEvent("nodedrop", de);
13907     },
13908     
13909     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13910         if(this.tree.hlDrop){
13911             dropNode.ui.focus();
13912             dropNode.ui.highlight();
13913         }
13914         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13915     },
13916     
13917     getTree : function(){
13918         return this.tree;
13919     },
13920     
13921     removeDropIndicators : function(n){
13922         if(n && n.ddel){
13923             var el = n.ddel;
13924             Roo.fly(el).removeClass([
13925                     "x-tree-drag-insert-above",
13926                     "x-tree-drag-insert-below",
13927                     "x-tree-drag-append"]);
13928             this.lastInsertClass = "_noclass";
13929         }
13930     },
13931     
13932     beforeDragDrop : function(target, e, id){
13933         this.cancelExpand();
13934         return true;
13935     },
13936     
13937     afterRepair : function(data){
13938         if(data && Roo.enableFx){
13939             data.node.ui.highlight();
13940         }
13941         this.hideProxy();
13942     } 
13943     
13944 });
13945
13946 }
13947 /*
13948  * Based on:
13949  * Ext JS Library 1.1.1
13950  * Copyright(c) 2006-2007, Ext JS, LLC.
13951  *
13952  * Originally Released Under LGPL - original licence link has changed is not relivant.
13953  *
13954  * Fork - LGPL
13955  * <script type="text/javascript">
13956  */
13957  
13958
13959 if(Roo.dd.DragZone){
13960 Roo.tree.TreeDragZone = function(tree, config){
13961     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13962     this.tree = tree;
13963 };
13964
13965 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13966     ddGroup : "TreeDD",
13967    
13968     onBeforeDrag : function(data, e){
13969         var n = data.node;
13970         return n && n.draggable && !n.disabled;
13971     },
13972      
13973     
13974     onInitDrag : function(e){
13975         var data = this.dragData;
13976         this.tree.getSelectionModel().select(data.node);
13977         this.proxy.update("");
13978         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13979         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13980     },
13981     
13982     getRepairXY : function(e, data){
13983         return data.node.ui.getDDRepairXY();
13984     },
13985     
13986     onEndDrag : function(data, e){
13987         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13988         
13989         
13990     },
13991     
13992     onValidDrop : function(dd, e, id){
13993         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13994         this.hideProxy();
13995     },
13996     
13997     beforeInvalidDrop : function(e, id){
13998         // this scrolls the original position back into view
13999         var sm = this.tree.getSelectionModel();
14000         sm.clearSelections();
14001         sm.select(this.dragData.node);
14002     }
14003 });
14004 }/*
14005  * Based on:
14006  * Ext JS Library 1.1.1
14007  * Copyright(c) 2006-2007, Ext JS, LLC.
14008  *
14009  * Originally Released Under LGPL - original licence link has changed is not relivant.
14010  *
14011  * Fork - LGPL
14012  * <script type="text/javascript">
14013  */
14014 /**
14015  * @class Roo.tree.TreeEditor
14016  * @extends Roo.Editor
14017  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14018  * as the editor field.
14019  * @constructor
14020  * @param {Object} config (used to be the tree panel.)
14021  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14022  * 
14023  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14024  * @cfg {Roo.form.TextField} field [required] The field configuration
14025  *
14026  * 
14027  */
14028 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14029     var tree = config;
14030     var field;
14031     if (oldconfig) { // old style..
14032         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14033     } else {
14034         // new style..
14035         tree = config.tree;
14036         config.field = config.field  || {};
14037         config.field.xtype = 'TextField';
14038         field = Roo.factory(config.field, Roo.form);
14039     }
14040     config = config || {};
14041     
14042     
14043     this.addEvents({
14044         /**
14045          * @event beforenodeedit
14046          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14047          * false from the handler of this event.
14048          * @param {Editor} this
14049          * @param {Roo.tree.Node} node 
14050          */
14051         "beforenodeedit" : true
14052     });
14053     
14054     //Roo.log(config);
14055     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14056
14057     this.tree = tree;
14058
14059     tree.on('beforeclick', this.beforeNodeClick, this);
14060     tree.getTreeEl().on('mousedown', this.hide, this);
14061     this.on('complete', this.updateNode, this);
14062     this.on('beforestartedit', this.fitToTree, this);
14063     this.on('startedit', this.bindScroll, this, {delay:10});
14064     this.on('specialkey', this.onSpecialKey, this);
14065 };
14066
14067 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14068     /**
14069      * @cfg {String} alignment
14070      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14071      */
14072     alignment: "l-l",
14073     // inherit
14074     autoSize: false,
14075     /**
14076      * @cfg {Boolean} hideEl
14077      * True to hide the bound element while the editor is displayed (defaults to false)
14078      */
14079     hideEl : false,
14080     /**
14081      * @cfg {String} cls
14082      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14083      */
14084     cls: "x-small-editor x-tree-editor",
14085     /**
14086      * @cfg {Boolean} shim
14087      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14088      */
14089     shim:false,
14090     // inherit
14091     shadow:"frame",
14092     /**
14093      * @cfg {Number} maxWidth
14094      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14095      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14096      * scroll and client offsets into account prior to each edit.
14097      */
14098     maxWidth: 250,
14099
14100     editDelay : 350,
14101
14102     // private
14103     fitToTree : function(ed, el){
14104         var td = this.tree.getTreeEl().dom, nd = el.dom;
14105         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14106             td.scrollLeft = nd.offsetLeft;
14107         }
14108         var w = Math.min(
14109                 this.maxWidth,
14110                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14111         this.setSize(w, '');
14112         
14113         return this.fireEvent('beforenodeedit', this, this.editNode);
14114         
14115     },
14116
14117     // private
14118     triggerEdit : function(node){
14119         this.completeEdit();
14120         this.editNode = node;
14121         this.startEdit(node.ui.textNode, node.text);
14122     },
14123
14124     // private
14125     bindScroll : function(){
14126         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14127     },
14128
14129     // private
14130     beforeNodeClick : function(node, e){
14131         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14132         this.lastClick = new Date();
14133         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14134             e.stopEvent();
14135             this.triggerEdit(node);
14136             return false;
14137         }
14138         return true;
14139     },
14140
14141     // private
14142     updateNode : function(ed, value){
14143         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14144         this.editNode.setText(value);
14145     },
14146
14147     // private
14148     onHide : function(){
14149         Roo.tree.TreeEditor.superclass.onHide.call(this);
14150         if(this.editNode){
14151             this.editNode.ui.focus();
14152         }
14153     },
14154
14155     // private
14156     onSpecialKey : function(field, e){
14157         var k = e.getKey();
14158         if(k == e.ESC){
14159             e.stopEvent();
14160             this.cancelEdit();
14161         }else if(k == e.ENTER && !e.hasModifier()){
14162             e.stopEvent();
14163             this.completeEdit();
14164         }
14165     }
14166 });//<Script type="text/javascript">
14167 /*
14168  * Based on:
14169  * Ext JS Library 1.1.1
14170  * Copyright(c) 2006-2007, Ext JS, LLC.
14171  *
14172  * Originally Released Under LGPL - original licence link has changed is not relivant.
14173  *
14174  * Fork - LGPL
14175  * <script type="text/javascript">
14176  */
14177  
14178 /**
14179  * Not documented??? - probably should be...
14180  */
14181
14182 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14183     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14184     
14185     renderElements : function(n, a, targetNode, bulkRender){
14186         //consel.log("renderElements?");
14187         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14188
14189         var t = n.getOwnerTree();
14190         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14191         
14192         var cols = t.columns;
14193         var bw = t.borderWidth;
14194         var c = cols[0];
14195         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14196          var cb = typeof a.checked == "boolean";
14197         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14198         var colcls = 'x-t-' + tid + '-c0';
14199         var buf = [
14200             '<li class="x-tree-node">',
14201             
14202                 
14203                 '<div class="x-tree-node-el ', a.cls,'">',
14204                     // extran...
14205                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14206                 
14207                 
14208                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14209                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14210                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14211                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14212                            (a.iconCls ? ' '+a.iconCls : ''),
14213                            '" unselectable="on" />',
14214                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14215                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14216                              
14217                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14218                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14219                             '<span unselectable="on" qtip="' + tx + '">',
14220                              tx,
14221                              '</span></a>' ,
14222                     '</div>',
14223                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14224                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14225                  ];
14226         for(var i = 1, len = cols.length; i < len; i++){
14227             c = cols[i];
14228             colcls = 'x-t-' + tid + '-c' +i;
14229             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14230             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14231                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14232                       "</div>");
14233          }
14234          
14235          buf.push(
14236             '</a>',
14237             '<div class="x-clear"></div></div>',
14238             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14239             "</li>");
14240         
14241         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14242             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14243                                 n.nextSibling.ui.getEl(), buf.join(""));
14244         }else{
14245             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14246         }
14247         var el = this.wrap.firstChild;
14248         this.elRow = el;
14249         this.elNode = el.firstChild;
14250         this.ranchor = el.childNodes[1];
14251         this.ctNode = this.wrap.childNodes[1];
14252         var cs = el.firstChild.childNodes;
14253         this.indentNode = cs[0];
14254         this.ecNode = cs[1];
14255         this.iconNode = cs[2];
14256         var index = 3;
14257         if(cb){
14258             this.checkbox = cs[3];
14259             index++;
14260         }
14261         this.anchor = cs[index];
14262         
14263         this.textNode = cs[index].firstChild;
14264         
14265         //el.on("click", this.onClick, this);
14266         //el.on("dblclick", this.onDblClick, this);
14267         
14268         
14269        // console.log(this);
14270     },
14271     initEvents : function(){
14272         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14273         
14274             
14275         var a = this.ranchor;
14276
14277         var el = Roo.get(a);
14278
14279         if(Roo.isOpera){ // opera render bug ignores the CSS
14280             el.setStyle("text-decoration", "none");
14281         }
14282
14283         el.on("click", this.onClick, this);
14284         el.on("dblclick", this.onDblClick, this);
14285         el.on("contextmenu", this.onContextMenu, this);
14286         
14287     },
14288     
14289     /*onSelectedChange : function(state){
14290         if(state){
14291             this.focus();
14292             this.addClass("x-tree-selected");
14293         }else{
14294             //this.blur();
14295             this.removeClass("x-tree-selected");
14296         }
14297     },*/
14298     addClass : function(cls){
14299         if(this.elRow){
14300             Roo.fly(this.elRow).addClass(cls);
14301         }
14302         
14303     },
14304     
14305     
14306     removeClass : function(cls){
14307         if(this.elRow){
14308             Roo.fly(this.elRow).removeClass(cls);
14309         }
14310     }
14311
14312     
14313     
14314 });//<Script type="text/javascript">
14315
14316 /*
14317  * Based on:
14318  * Ext JS Library 1.1.1
14319  * Copyright(c) 2006-2007, Ext JS, LLC.
14320  *
14321  * Originally Released Under LGPL - original licence link has changed is not relivant.
14322  *
14323  * Fork - LGPL
14324  * <script type="text/javascript">
14325  */
14326  
14327
14328 /**
14329  * @class Roo.tree.ColumnTree
14330  * @extends Roo.tree.TreePanel
14331  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14332  * @cfg {int} borderWidth  compined right/left border allowance
14333  * @constructor
14334  * @param {String/HTMLElement/Element} el The container element
14335  * @param {Object} config
14336  */
14337 Roo.tree.ColumnTree =  function(el, config)
14338 {
14339    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14340    this.addEvents({
14341         /**
14342         * @event resize
14343         * Fire this event on a container when it resizes
14344         * @param {int} w Width
14345         * @param {int} h Height
14346         */
14347        "resize" : true
14348     });
14349     this.on('resize', this.onResize, this);
14350 };
14351
14352 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14353     //lines:false,
14354     
14355     
14356     borderWidth: Roo.isBorderBox ? 0 : 2, 
14357     headEls : false,
14358     
14359     render : function(){
14360         // add the header.....
14361        
14362         Roo.tree.ColumnTree.superclass.render.apply(this);
14363         
14364         this.el.addClass('x-column-tree');
14365         
14366         this.headers = this.el.createChild(
14367             {cls:'x-tree-headers'},this.innerCt.dom);
14368    
14369         var cols = this.columns, c;
14370         var totalWidth = 0;
14371         this.headEls = [];
14372         var  len = cols.length;
14373         for(var i = 0; i < len; i++){
14374              c = cols[i];
14375              totalWidth += c.width;
14376             this.headEls.push(this.headers.createChild({
14377                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14378                  cn: {
14379                      cls:'x-tree-hd-text',
14380                      html: c.header
14381                  },
14382                  style:'width:'+(c.width-this.borderWidth)+'px;'
14383              }));
14384         }
14385         this.headers.createChild({cls:'x-clear'});
14386         // prevent floats from wrapping when clipped
14387         this.headers.setWidth(totalWidth);
14388         //this.innerCt.setWidth(totalWidth);
14389         this.innerCt.setStyle({ overflow: 'auto' });
14390         this.onResize(this.width, this.height);
14391              
14392         
14393     },
14394     onResize : function(w,h)
14395     {
14396         this.height = h;
14397         this.width = w;
14398         // resize cols..
14399         this.innerCt.setWidth(this.width);
14400         this.innerCt.setHeight(this.height-20);
14401         
14402         // headers...
14403         var cols = this.columns, c;
14404         var totalWidth = 0;
14405         var expEl = false;
14406         var len = cols.length;
14407         for(var i = 0; i < len; i++){
14408             c = cols[i];
14409             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14410                 // it's the expander..
14411                 expEl  = this.headEls[i];
14412                 continue;
14413             }
14414             totalWidth += c.width;
14415             
14416         }
14417         if (expEl) {
14418             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14419         }
14420         this.headers.setWidth(w-20);
14421
14422         
14423         
14424         
14425     }
14426 });
14427 /*
14428  * Based on:
14429  * Ext JS Library 1.1.1
14430  * Copyright(c) 2006-2007, Ext JS, LLC.
14431  *
14432  * Originally Released Under LGPL - original licence link has changed is not relivant.
14433  *
14434  * Fork - LGPL
14435  * <script type="text/javascript">
14436  */
14437  
14438 /**
14439  * @class Roo.menu.Menu
14440  * @extends Roo.util.Observable
14441  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14442  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14443  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14444  * @constructor
14445  * Creates a new Menu
14446  * @param {Object} config Configuration options
14447  */
14448 Roo.menu.Menu = function(config){
14449     
14450     Roo.menu.Menu.superclass.constructor.call(this, config);
14451     
14452     this.id = this.id || Roo.id();
14453     this.addEvents({
14454         /**
14455          * @event beforeshow
14456          * Fires before this menu is displayed
14457          * @param {Roo.menu.Menu} this
14458          */
14459         beforeshow : true,
14460         /**
14461          * @event beforehide
14462          * Fires before this menu is hidden
14463          * @param {Roo.menu.Menu} this
14464          */
14465         beforehide : true,
14466         /**
14467          * @event show
14468          * Fires after this menu is displayed
14469          * @param {Roo.menu.Menu} this
14470          */
14471         show : true,
14472         /**
14473          * @event hide
14474          * Fires after this menu is hidden
14475          * @param {Roo.menu.Menu} this
14476          */
14477         hide : true,
14478         /**
14479          * @event click
14480          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14481          * @param {Roo.menu.Menu} this
14482          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14483          * @param {Roo.EventObject} e
14484          */
14485         click : true,
14486         /**
14487          * @event mouseover
14488          * Fires when the mouse is hovering over this menu
14489          * @param {Roo.menu.Menu} this
14490          * @param {Roo.EventObject} e
14491          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14492          */
14493         mouseover : true,
14494         /**
14495          * @event mouseout
14496          * Fires when the mouse exits this menu
14497          * @param {Roo.menu.Menu} this
14498          * @param {Roo.EventObject} e
14499          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14500          */
14501         mouseout : true,
14502         /**
14503          * @event itemclick
14504          * Fires when a menu item contained in this menu is clicked
14505          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14506          * @param {Roo.EventObject} e
14507          */
14508         itemclick: true
14509     });
14510     if (this.registerMenu) {
14511         Roo.menu.MenuMgr.register(this);
14512     }
14513     
14514     var mis = this.items;
14515     this.items = new Roo.util.MixedCollection();
14516     if(mis){
14517         this.add.apply(this, mis);
14518     }
14519 };
14520
14521 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14522     /**
14523      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14524      */
14525     minWidth : 120,
14526     /**
14527      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14528      * for bottom-right shadow (defaults to "sides")
14529      */
14530     shadow : "sides",
14531     /**
14532      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14533      * this menu (defaults to "tl-tr?")
14534      */
14535     subMenuAlign : "tl-tr?",
14536     /**
14537      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14538      * relative to its element of origin (defaults to "tl-bl?")
14539      */
14540     defaultAlign : "tl-bl?",
14541     /**
14542      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14543      */
14544     allowOtherMenus : false,
14545     /**
14546      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14547      */
14548     registerMenu : true,
14549
14550     hidden:true,
14551
14552     // private
14553     render : function(){
14554         if(this.el){
14555             return;
14556         }
14557         var el = this.el = new Roo.Layer({
14558             cls: "x-menu",
14559             shadow:this.shadow,
14560             constrain: false,
14561             parentEl: this.parentEl || document.body,
14562             zindex:15000
14563         });
14564
14565         this.keyNav = new Roo.menu.MenuNav(this);
14566
14567         if(this.plain){
14568             el.addClass("x-menu-plain");
14569         }
14570         if(this.cls){
14571             el.addClass(this.cls);
14572         }
14573         // generic focus element
14574         this.focusEl = el.createChild({
14575             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14576         });
14577         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14578         //disabling touch- as it's causing issues ..
14579         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14580         ul.on('click'   , this.onClick, this);
14581         
14582         
14583         ul.on("mouseover", this.onMouseOver, this);
14584         ul.on("mouseout", this.onMouseOut, this);
14585         this.items.each(function(item){
14586             if (item.hidden) {
14587                 return;
14588             }
14589             
14590             var li = document.createElement("li");
14591             li.className = "x-menu-list-item";
14592             ul.dom.appendChild(li);
14593             item.render(li, this);
14594         }, this);
14595         this.ul = ul;
14596         this.autoWidth();
14597     },
14598
14599     // private
14600     autoWidth : function(){
14601         var el = this.el, ul = this.ul;
14602         if(!el){
14603             return;
14604         }
14605         var w = this.width;
14606         if(w){
14607             el.setWidth(w);
14608         }else if(Roo.isIE){
14609             el.setWidth(this.minWidth);
14610             var t = el.dom.offsetWidth; // force recalc
14611             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14612         }
14613     },
14614
14615     // private
14616     delayAutoWidth : function(){
14617         if(this.rendered){
14618             if(!this.awTask){
14619                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14620             }
14621             this.awTask.delay(20);
14622         }
14623     },
14624
14625     // private
14626     findTargetItem : function(e){
14627         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14628         if(t && t.menuItemId){
14629             return this.items.get(t.menuItemId);
14630         }
14631     },
14632
14633     // private
14634     onClick : function(e){
14635         Roo.log("menu.onClick");
14636         var t = this.findTargetItem(e);
14637         if(!t){
14638             return;
14639         }
14640         Roo.log(e);
14641         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14642             if(t == this.activeItem && t.shouldDeactivate(e)){
14643                 this.activeItem.deactivate();
14644                 delete this.activeItem;
14645                 return;
14646             }
14647             if(t.canActivate){
14648                 this.setActiveItem(t, true);
14649             }
14650             return;
14651             
14652             
14653         }
14654         
14655         t.onClick(e);
14656         this.fireEvent("click", this, t, e);
14657     },
14658
14659     // private
14660     setActiveItem : function(item, autoExpand){
14661         if(item != this.activeItem){
14662             if(this.activeItem){
14663                 this.activeItem.deactivate();
14664             }
14665             this.activeItem = item;
14666             item.activate(autoExpand);
14667         }else if(autoExpand){
14668             item.expandMenu();
14669         }
14670     },
14671
14672     // private
14673     tryActivate : function(start, step){
14674         var items = this.items;
14675         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14676             var item = items.get(i);
14677             if(!item.disabled && item.canActivate){
14678                 this.setActiveItem(item, false);
14679                 return item;
14680             }
14681         }
14682         return false;
14683     },
14684
14685     // private
14686     onMouseOver : function(e){
14687         var t;
14688         if(t = this.findTargetItem(e)){
14689             if(t.canActivate && !t.disabled){
14690                 this.setActiveItem(t, true);
14691             }
14692         }
14693         this.fireEvent("mouseover", this, e, t);
14694     },
14695
14696     // private
14697     onMouseOut : function(e){
14698         var t;
14699         if(t = this.findTargetItem(e)){
14700             if(t == this.activeItem && t.shouldDeactivate(e)){
14701                 this.activeItem.deactivate();
14702                 delete this.activeItem;
14703             }
14704         }
14705         this.fireEvent("mouseout", this, e, t);
14706     },
14707
14708     /**
14709      * Read-only.  Returns true if the menu is currently displayed, else false.
14710      * @type Boolean
14711      */
14712     isVisible : function(){
14713         return this.el && !this.hidden;
14714     },
14715
14716     /**
14717      * Displays this menu relative to another element
14718      * @param {String/HTMLElement/Roo.Element} element The element to align to
14719      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14720      * the element (defaults to this.defaultAlign)
14721      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14722      */
14723     show : function(el, pos, parentMenu){
14724         this.parentMenu = parentMenu;
14725         if(!this.el){
14726             this.render();
14727         }
14728         this.fireEvent("beforeshow", this);
14729         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14730     },
14731
14732     /**
14733      * Displays this menu at a specific xy position
14734      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14735      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14736      */
14737     showAt : function(xy, parentMenu, /* private: */_e){
14738         this.parentMenu = parentMenu;
14739         if(!this.el){
14740             this.render();
14741         }
14742         if(_e !== false){
14743             this.fireEvent("beforeshow", this);
14744             xy = this.el.adjustForConstraints(xy);
14745         }
14746         this.el.setXY(xy);
14747         this.el.show();
14748         this.hidden = false;
14749         this.focus();
14750         this.fireEvent("show", this);
14751     },
14752
14753     focus : function(){
14754         if(!this.hidden){
14755             this.doFocus.defer(50, this);
14756         }
14757     },
14758
14759     doFocus : function(){
14760         if(!this.hidden){
14761             this.focusEl.focus();
14762         }
14763     },
14764
14765     /**
14766      * Hides this menu and optionally all parent menus
14767      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14768      */
14769     hide : function(deep){
14770         if(this.el && this.isVisible()){
14771             this.fireEvent("beforehide", this);
14772             if(this.activeItem){
14773                 this.activeItem.deactivate();
14774                 this.activeItem = null;
14775             }
14776             this.el.hide();
14777             this.hidden = true;
14778             this.fireEvent("hide", this);
14779         }
14780         if(deep === true && this.parentMenu){
14781             this.parentMenu.hide(true);
14782         }
14783     },
14784
14785     /**
14786      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14787      * Any of the following are valid:
14788      * <ul>
14789      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14790      * <li>An HTMLElement object which will be converted to a menu item</li>
14791      * <li>A menu item config object that will be created as a new menu item</li>
14792      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14793      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14794      * </ul>
14795      * Usage:
14796      * <pre><code>
14797 // Create the menu
14798 var menu = new Roo.menu.Menu();
14799
14800 // Create a menu item to add by reference
14801 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14802
14803 // Add a bunch of items at once using different methods.
14804 // Only the last item added will be returned.
14805 var item = menu.add(
14806     menuItem,                // add existing item by ref
14807     'Dynamic Item',          // new TextItem
14808     '-',                     // new separator
14809     { text: 'Config Item' }  // new item by config
14810 );
14811 </code></pre>
14812      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14813      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14814      */
14815     add : function(){
14816         var a = arguments, l = a.length, item;
14817         for(var i = 0; i < l; i++){
14818             var el = a[i];
14819             if ((typeof(el) == "object") && el.xtype && el.xns) {
14820                 el = Roo.factory(el, Roo.menu);
14821             }
14822             
14823             if(el.render){ // some kind of Item
14824                 item = this.addItem(el);
14825             }else if(typeof el == "string"){ // string
14826                 if(el == "separator" || el == "-"){
14827                     item = this.addSeparator();
14828                 }else{
14829                     item = this.addText(el);
14830                 }
14831             }else if(el.tagName || el.el){ // element
14832                 item = this.addElement(el);
14833             }else if(typeof el == "object"){ // must be menu item config?
14834                 item = this.addMenuItem(el);
14835             }
14836         }
14837         return item;
14838     },
14839
14840     /**
14841      * Returns this menu's underlying {@link Roo.Element} object
14842      * @return {Roo.Element} The element
14843      */
14844     getEl : function(){
14845         if(!this.el){
14846             this.render();
14847         }
14848         return this.el;
14849     },
14850
14851     /**
14852      * Adds a separator bar to the menu
14853      * @return {Roo.menu.Item} The menu item that was added
14854      */
14855     addSeparator : function(){
14856         return this.addItem(new Roo.menu.Separator());
14857     },
14858
14859     /**
14860      * Adds an {@link Roo.Element} object to the menu
14861      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14862      * @return {Roo.menu.Item} The menu item that was added
14863      */
14864     addElement : function(el){
14865         return this.addItem(new Roo.menu.BaseItem(el));
14866     },
14867
14868     /**
14869      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14870      * @param {Roo.menu.Item} item The menu item to add
14871      * @return {Roo.menu.Item} The menu item that was added
14872      */
14873     addItem : function(item){
14874         this.items.add(item);
14875         if(this.ul){
14876             var li = document.createElement("li");
14877             li.className = "x-menu-list-item";
14878             this.ul.dom.appendChild(li);
14879             item.render(li, this);
14880             this.delayAutoWidth();
14881         }
14882         return item;
14883     },
14884
14885     /**
14886      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14887      * @param {Object} config A MenuItem config object
14888      * @return {Roo.menu.Item} The menu item that was added
14889      */
14890     addMenuItem : function(config){
14891         if(!(config instanceof Roo.menu.Item)){
14892             if(typeof config.checked == "boolean"){ // must be check menu item config?
14893                 config = new Roo.menu.CheckItem(config);
14894             }else{
14895                 config = new Roo.menu.Item(config);
14896             }
14897         }
14898         return this.addItem(config);
14899     },
14900
14901     /**
14902      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14903      * @param {String} text The text to display in the menu item
14904      * @return {Roo.menu.Item} The menu item that was added
14905      */
14906     addText : function(text){
14907         return this.addItem(new Roo.menu.TextItem({ text : text }));
14908     },
14909
14910     /**
14911      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14912      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14913      * @param {Roo.menu.Item} item The menu item to add
14914      * @return {Roo.menu.Item} The menu item that was added
14915      */
14916     insert : function(index, item){
14917         this.items.insert(index, item);
14918         if(this.ul){
14919             var li = document.createElement("li");
14920             li.className = "x-menu-list-item";
14921             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14922             item.render(li, this);
14923             this.delayAutoWidth();
14924         }
14925         return item;
14926     },
14927
14928     /**
14929      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14930      * @param {Roo.menu.Item} item The menu item to remove
14931      */
14932     remove : function(item){
14933         this.items.removeKey(item.id);
14934         item.destroy();
14935     },
14936
14937     /**
14938      * Removes and destroys all items in the menu
14939      */
14940     removeAll : function(){
14941         var f;
14942         while(f = this.items.first()){
14943             this.remove(f);
14944         }
14945     }
14946 });
14947
14948 // MenuNav is a private utility class used internally by the Menu
14949 Roo.menu.MenuNav = function(menu){
14950     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14951     this.scope = this.menu = menu;
14952 };
14953
14954 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14955     doRelay : function(e, h){
14956         var k = e.getKey();
14957         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14958             this.menu.tryActivate(0, 1);
14959             return false;
14960         }
14961         return h.call(this.scope || this, e, this.menu);
14962     },
14963
14964     up : function(e, m){
14965         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14966             m.tryActivate(m.items.length-1, -1);
14967         }
14968     },
14969
14970     down : function(e, m){
14971         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14972             m.tryActivate(0, 1);
14973         }
14974     },
14975
14976     right : function(e, m){
14977         if(m.activeItem){
14978             m.activeItem.expandMenu(true);
14979         }
14980     },
14981
14982     left : function(e, m){
14983         m.hide();
14984         if(m.parentMenu && m.parentMenu.activeItem){
14985             m.parentMenu.activeItem.activate();
14986         }
14987     },
14988
14989     enter : function(e, m){
14990         if(m.activeItem){
14991             e.stopPropagation();
14992             m.activeItem.onClick(e);
14993             m.fireEvent("click", this, m.activeItem);
14994             return true;
14995         }
14996     }
14997 });/*
14998  * Based on:
14999  * Ext JS Library 1.1.1
15000  * Copyright(c) 2006-2007, Ext JS, LLC.
15001  *
15002  * Originally Released Under LGPL - original licence link has changed is not relivant.
15003  *
15004  * Fork - LGPL
15005  * <script type="text/javascript">
15006  */
15007  
15008 /**
15009  * @class Roo.menu.MenuMgr
15010  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15011  * @static
15012  */
15013 Roo.menu.MenuMgr = function(){
15014    var menus, active, groups = {}, attached = false, lastShow = new Date();
15015
15016    // private - called when first menu is created
15017    function init(){
15018        menus = {};
15019        active = new Roo.util.MixedCollection();
15020        Roo.get(document).addKeyListener(27, function(){
15021            if(active.length > 0){
15022                hideAll();
15023            }
15024        });
15025    }
15026
15027    // private
15028    function hideAll(){
15029        if(active && active.length > 0){
15030            var c = active.clone();
15031            c.each(function(m){
15032                m.hide();
15033            });
15034        }
15035    }
15036
15037    // private
15038    function onHide(m){
15039        active.remove(m);
15040        if(active.length < 1){
15041            Roo.get(document).un("mousedown", onMouseDown);
15042            attached = false;
15043        }
15044    }
15045
15046    // private
15047    function onShow(m){
15048        var last = active.last();
15049        lastShow = new Date();
15050        active.add(m);
15051        if(!attached){
15052            Roo.get(document).on("mousedown", onMouseDown);
15053            attached = true;
15054        }
15055        if(m.parentMenu){
15056           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15057           m.parentMenu.activeChild = m;
15058        }else if(last && last.isVisible()){
15059           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15060        }
15061    }
15062
15063    // private
15064    function onBeforeHide(m){
15065        if(m.activeChild){
15066            m.activeChild.hide();
15067        }
15068        if(m.autoHideTimer){
15069            clearTimeout(m.autoHideTimer);
15070            delete m.autoHideTimer;
15071        }
15072    }
15073
15074    // private
15075    function onBeforeShow(m){
15076        var pm = m.parentMenu;
15077        if(!pm && !m.allowOtherMenus){
15078            hideAll();
15079        }else if(pm && pm.activeChild && active != m){
15080            pm.activeChild.hide();
15081        }
15082    }
15083
15084    // private
15085    function onMouseDown(e){
15086        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15087            hideAll();
15088        }
15089    }
15090
15091    // private
15092    function onBeforeCheck(mi, state){
15093        if(state){
15094            var g = groups[mi.group];
15095            for(var i = 0, l = g.length; i < l; i++){
15096                if(g[i] != mi){
15097                    g[i].setChecked(false);
15098                }
15099            }
15100        }
15101    }
15102
15103    return {
15104
15105        /**
15106         * Hides all menus that are currently visible
15107         */
15108        hideAll : function(){
15109             hideAll();  
15110        },
15111
15112        // private
15113        register : function(menu){
15114            if(!menus){
15115                init();
15116            }
15117            menus[menu.id] = menu;
15118            menu.on("beforehide", onBeforeHide);
15119            menu.on("hide", onHide);
15120            menu.on("beforeshow", onBeforeShow);
15121            menu.on("show", onShow);
15122            var g = menu.group;
15123            if(g && menu.events["checkchange"]){
15124                if(!groups[g]){
15125                    groups[g] = [];
15126                }
15127                groups[g].push(menu);
15128                menu.on("checkchange", onCheck);
15129            }
15130        },
15131
15132         /**
15133          * Returns a {@link Roo.menu.Menu} object
15134          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15135          * be used to generate and return a new Menu instance.
15136          */
15137        get : function(menu){
15138            if(typeof menu == "string"){ // menu id
15139                return menus[menu];
15140            }else if(menu.events){  // menu instance
15141                return menu;
15142            }else if(typeof menu.length == 'number'){ // array of menu items?
15143                return new Roo.menu.Menu({items:menu});
15144            }else{ // otherwise, must be a config
15145                return new Roo.menu.Menu(menu);
15146            }
15147        },
15148
15149        // private
15150        unregister : function(menu){
15151            delete menus[menu.id];
15152            menu.un("beforehide", onBeforeHide);
15153            menu.un("hide", onHide);
15154            menu.un("beforeshow", onBeforeShow);
15155            menu.un("show", onShow);
15156            var g = menu.group;
15157            if(g && menu.events["checkchange"]){
15158                groups[g].remove(menu);
15159                menu.un("checkchange", onCheck);
15160            }
15161        },
15162
15163        // private
15164        registerCheckable : function(menuItem){
15165            var g = menuItem.group;
15166            if(g){
15167                if(!groups[g]){
15168                    groups[g] = [];
15169                }
15170                groups[g].push(menuItem);
15171                menuItem.on("beforecheckchange", onBeforeCheck);
15172            }
15173        },
15174
15175        // private
15176        unregisterCheckable : function(menuItem){
15177            var g = menuItem.group;
15178            if(g){
15179                groups[g].remove(menuItem);
15180                menuItem.un("beforecheckchange", onBeforeCheck);
15181            }
15182        }
15183    };
15184 }();/*
15185  * Based on:
15186  * Ext JS Library 1.1.1
15187  * Copyright(c) 2006-2007, Ext JS, LLC.
15188  *
15189  * Originally Released Under LGPL - original licence link has changed is not relivant.
15190  *
15191  * Fork - LGPL
15192  * <script type="text/javascript">
15193  */
15194  
15195
15196 /**
15197  * @class Roo.menu.BaseItem
15198  * @extends Roo.Component
15199  * @abstract
15200  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15201  * management and base configuration options shared by all menu components.
15202  * @constructor
15203  * Creates a new BaseItem
15204  * @param {Object} config Configuration options
15205  */
15206 Roo.menu.BaseItem = function(config){
15207     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15208
15209     this.addEvents({
15210         /**
15211          * @event click
15212          * Fires when this item is clicked
15213          * @param {Roo.menu.BaseItem} this
15214          * @param {Roo.EventObject} e
15215          */
15216         click: true,
15217         /**
15218          * @event activate
15219          * Fires when this item is activated
15220          * @param {Roo.menu.BaseItem} this
15221          */
15222         activate : true,
15223         /**
15224          * @event deactivate
15225          * Fires when this item is deactivated
15226          * @param {Roo.menu.BaseItem} this
15227          */
15228         deactivate : true
15229     });
15230
15231     if(this.handler){
15232         this.on("click", this.handler, this.scope, true);
15233     }
15234 };
15235
15236 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15237     /**
15238      * @cfg {Function} handler
15239      * A function that will handle the click event of this menu item (defaults to undefined)
15240      */
15241     /**
15242      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15243      */
15244     canActivate : false,
15245     
15246      /**
15247      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15248      */
15249     hidden: false,
15250     
15251     /**
15252      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15253      */
15254     activeClass : "x-menu-item-active",
15255     /**
15256      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15257      */
15258     hideOnClick : true,
15259     /**
15260      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15261      */
15262     hideDelay : 100,
15263
15264     // private
15265     ctype: "Roo.menu.BaseItem",
15266
15267     // private
15268     actionMode : "container",
15269
15270     // private
15271     render : function(container, parentMenu){
15272         this.parentMenu = parentMenu;
15273         Roo.menu.BaseItem.superclass.render.call(this, container);
15274         this.container.menuItemId = this.id;
15275     },
15276
15277     // private
15278     onRender : function(container, position){
15279         this.el = Roo.get(this.el);
15280         container.dom.appendChild(this.el.dom);
15281     },
15282
15283     // private
15284     onClick : function(e){
15285         if(!this.disabled && this.fireEvent("click", this, e) !== false
15286                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15287             this.handleClick(e);
15288         }else{
15289             e.stopEvent();
15290         }
15291     },
15292
15293     // private
15294     activate : function(){
15295         if(this.disabled){
15296             return false;
15297         }
15298         var li = this.container;
15299         li.addClass(this.activeClass);
15300         this.region = li.getRegion().adjust(2, 2, -2, -2);
15301         this.fireEvent("activate", this);
15302         return true;
15303     },
15304
15305     // private
15306     deactivate : function(){
15307         this.container.removeClass(this.activeClass);
15308         this.fireEvent("deactivate", this);
15309     },
15310
15311     // private
15312     shouldDeactivate : function(e){
15313         return !this.region || !this.region.contains(e.getPoint());
15314     },
15315
15316     // private
15317     handleClick : function(e){
15318         if(this.hideOnClick){
15319             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15320         }
15321     },
15322
15323     // private
15324     expandMenu : function(autoActivate){
15325         // do nothing
15326     },
15327
15328     // private
15329     hideMenu : function(){
15330         // do nothing
15331     }
15332 });/*
15333  * Based on:
15334  * Ext JS Library 1.1.1
15335  * Copyright(c) 2006-2007, Ext JS, LLC.
15336  *
15337  * Originally Released Under LGPL - original licence link has changed is not relivant.
15338  *
15339  * Fork - LGPL
15340  * <script type="text/javascript">
15341  */
15342  
15343 /**
15344  * @class Roo.menu.Adapter
15345  * @extends Roo.menu.BaseItem
15346  * @abstract
15347  * 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.
15348  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15349  * @constructor
15350  * Creates a new Adapter
15351  * @param {Object} config Configuration options
15352  */
15353 Roo.menu.Adapter = function(component, config){
15354     Roo.menu.Adapter.superclass.constructor.call(this, config);
15355     this.component = component;
15356 };
15357 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15358     // private
15359     canActivate : true,
15360
15361     // private
15362     onRender : function(container, position){
15363         this.component.render(container);
15364         this.el = this.component.getEl();
15365     },
15366
15367     // private
15368     activate : function(){
15369         if(this.disabled){
15370             return false;
15371         }
15372         this.component.focus();
15373         this.fireEvent("activate", this);
15374         return true;
15375     },
15376
15377     // private
15378     deactivate : function(){
15379         this.fireEvent("deactivate", this);
15380     },
15381
15382     // private
15383     disable : function(){
15384         this.component.disable();
15385         Roo.menu.Adapter.superclass.disable.call(this);
15386     },
15387
15388     // private
15389     enable : function(){
15390         this.component.enable();
15391         Roo.menu.Adapter.superclass.enable.call(this);
15392     }
15393 });/*
15394  * Based on:
15395  * Ext JS Library 1.1.1
15396  * Copyright(c) 2006-2007, Ext JS, LLC.
15397  *
15398  * Originally Released Under LGPL - original licence link has changed is not relivant.
15399  *
15400  * Fork - LGPL
15401  * <script type="text/javascript">
15402  */
15403
15404 /**
15405  * @class Roo.menu.TextItem
15406  * @extends Roo.menu.BaseItem
15407  * Adds a static text string to a menu, usually used as either a heading or group separator.
15408  * Note: old style constructor with text is still supported.
15409  * 
15410  * @constructor
15411  * Creates a new TextItem
15412  * @param {Object} cfg Configuration
15413  */
15414 Roo.menu.TextItem = function(cfg){
15415     if (typeof(cfg) == 'string') {
15416         this.text = cfg;
15417     } else {
15418         Roo.apply(this,cfg);
15419     }
15420     
15421     Roo.menu.TextItem.superclass.constructor.call(this);
15422 };
15423
15424 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15425     /**
15426      * @cfg {String} text Text to show on item.
15427      */
15428     text : '',
15429     
15430     /**
15431      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15432      */
15433     hideOnClick : false,
15434     /**
15435      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15436      */
15437     itemCls : "x-menu-text",
15438
15439     // private
15440     onRender : function(){
15441         var s = document.createElement("span");
15442         s.className = this.itemCls;
15443         s.innerHTML = this.text;
15444         this.el = s;
15445         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15446     }
15447 });/*
15448  * Based on:
15449  * Ext JS Library 1.1.1
15450  * Copyright(c) 2006-2007, Ext JS, LLC.
15451  *
15452  * Originally Released Under LGPL - original licence link has changed is not relivant.
15453  *
15454  * Fork - LGPL
15455  * <script type="text/javascript">
15456  */
15457
15458 /**
15459  * @class Roo.menu.Separator
15460  * @extends Roo.menu.BaseItem
15461  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15462  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15463  * @constructor
15464  * @param {Object} config Configuration options
15465  */
15466 Roo.menu.Separator = function(config){
15467     Roo.menu.Separator.superclass.constructor.call(this, config);
15468 };
15469
15470 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15471     /**
15472      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15473      */
15474     itemCls : "x-menu-sep",
15475     /**
15476      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15477      */
15478     hideOnClick : false,
15479
15480     // private
15481     onRender : function(li){
15482         var s = document.createElement("span");
15483         s.className = this.itemCls;
15484         s.innerHTML = "&#160;";
15485         this.el = s;
15486         li.addClass("x-menu-sep-li");
15487         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15488     }
15489 });/*
15490  * Based on:
15491  * Ext JS Library 1.1.1
15492  * Copyright(c) 2006-2007, Ext JS, LLC.
15493  *
15494  * Originally Released Under LGPL - original licence link has changed is not relivant.
15495  *
15496  * Fork - LGPL
15497  * <script type="text/javascript">
15498  */
15499 /**
15500  * @class Roo.menu.Item
15501  * @extends Roo.menu.BaseItem
15502  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15503  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15504  * activation and click handling.
15505  * @constructor
15506  * Creates a new Item
15507  * @param {Object} config Configuration options
15508  */
15509 Roo.menu.Item = function(config){
15510     Roo.menu.Item.superclass.constructor.call(this, config);
15511     if(this.menu){
15512         this.menu = Roo.menu.MenuMgr.get(this.menu);
15513     }
15514 };
15515 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15516     /**
15517      * @cfg {Roo.menu.Menu} menu
15518      * A Sub menu
15519      */
15520     /**
15521      * @cfg {String} text
15522      * The text to show on the menu item.
15523      */
15524     text: '',
15525      /**
15526      * @cfg {String} html to render in menu
15527      * The text to show on the menu item (HTML version).
15528      */
15529     html: '',
15530     /**
15531      * @cfg {String} icon
15532      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15533      */
15534     icon: undefined,
15535     /**
15536      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15537      */
15538     itemCls : "x-menu-item",
15539     /**
15540      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15541      */
15542     canActivate : true,
15543     /**
15544      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15545      */
15546     showDelay: 200,
15547     // doc'd in BaseItem
15548     hideDelay: 200,
15549
15550     // private
15551     ctype: "Roo.menu.Item",
15552     
15553     // private
15554     onRender : function(container, position){
15555         var el = document.createElement("a");
15556         el.hideFocus = true;
15557         el.unselectable = "on";
15558         el.href = this.href || "#";
15559         if(this.hrefTarget){
15560             el.target = this.hrefTarget;
15561         }
15562         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15563         
15564         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15565         
15566         el.innerHTML = String.format(
15567                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15568                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15569         this.el = el;
15570         Roo.menu.Item.superclass.onRender.call(this, container, position);
15571     },
15572
15573     /**
15574      * Sets the text to display in this menu item
15575      * @param {String} text The text to display
15576      * @param {Boolean} isHTML true to indicate text is pure html.
15577      */
15578     setText : function(text, isHTML){
15579         if (isHTML) {
15580             this.html = text;
15581         } else {
15582             this.text = text;
15583             this.html = '';
15584         }
15585         if(this.rendered){
15586             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15587      
15588             this.el.update(String.format(
15589                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15590                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15591             this.parentMenu.autoWidth();
15592         }
15593     },
15594
15595     // private
15596     handleClick : function(e){
15597         if(!this.href){ // if no link defined, stop the event automatically
15598             e.stopEvent();
15599         }
15600         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15601     },
15602
15603     // private
15604     activate : function(autoExpand){
15605         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15606             this.focus();
15607             if(autoExpand){
15608                 this.expandMenu();
15609             }
15610         }
15611         return true;
15612     },
15613
15614     // private
15615     shouldDeactivate : function(e){
15616         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15617             if(this.menu && this.menu.isVisible()){
15618                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15619             }
15620             return true;
15621         }
15622         return false;
15623     },
15624
15625     // private
15626     deactivate : function(){
15627         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15628         this.hideMenu();
15629     },
15630
15631     // private
15632     expandMenu : function(autoActivate){
15633         if(!this.disabled && this.menu){
15634             clearTimeout(this.hideTimer);
15635             delete this.hideTimer;
15636             if(!this.menu.isVisible() && !this.showTimer){
15637                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15638             }else if (this.menu.isVisible() && autoActivate){
15639                 this.menu.tryActivate(0, 1);
15640             }
15641         }
15642     },
15643
15644     // private
15645     deferExpand : function(autoActivate){
15646         delete this.showTimer;
15647         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15648         if(autoActivate){
15649             this.menu.tryActivate(0, 1);
15650         }
15651     },
15652
15653     // private
15654     hideMenu : function(){
15655         clearTimeout(this.showTimer);
15656         delete this.showTimer;
15657         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15658             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15659         }
15660     },
15661
15662     // private
15663     deferHide : function(){
15664         delete this.hideTimer;
15665         this.menu.hide();
15666     }
15667 });/*
15668  * Based on:
15669  * Ext JS Library 1.1.1
15670  * Copyright(c) 2006-2007, Ext JS, LLC.
15671  *
15672  * Originally Released Under LGPL - original licence link has changed is not relivant.
15673  *
15674  * Fork - LGPL
15675  * <script type="text/javascript">
15676  */
15677  
15678 /**
15679  * @class Roo.menu.CheckItem
15680  * @extends Roo.menu.Item
15681  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15682  * @constructor
15683  * Creates a new CheckItem
15684  * @param {Object} config Configuration options
15685  */
15686 Roo.menu.CheckItem = function(config){
15687     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15688     this.addEvents({
15689         /**
15690          * @event beforecheckchange
15691          * Fires before the checked value is set, providing an opportunity to cancel if needed
15692          * @param {Roo.menu.CheckItem} this
15693          * @param {Boolean} checked The new checked value that will be set
15694          */
15695         "beforecheckchange" : true,
15696         /**
15697          * @event checkchange
15698          * Fires after the checked value has been set
15699          * @param {Roo.menu.CheckItem} this
15700          * @param {Boolean} checked The checked value that was set
15701          */
15702         "checkchange" : true
15703     });
15704     if(this.checkHandler){
15705         this.on('checkchange', this.checkHandler, this.scope);
15706     }
15707 };
15708 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15709     /**
15710      * @cfg {String} group
15711      * All check items with the same group name will automatically be grouped into a single-select
15712      * radio button group (defaults to '')
15713      */
15714     /**
15715      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15716      */
15717     itemCls : "x-menu-item x-menu-check-item",
15718     /**
15719      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15720      */
15721     groupClass : "x-menu-group-item",
15722
15723     /**
15724      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15725      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15726      * initialized with checked = true will be rendered as checked.
15727      */
15728     checked: false,
15729
15730     // private
15731     ctype: "Roo.menu.CheckItem",
15732
15733     // private
15734     onRender : function(c){
15735         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15736         if(this.group){
15737             this.el.addClass(this.groupClass);
15738         }
15739         Roo.menu.MenuMgr.registerCheckable(this);
15740         if(this.checked){
15741             this.checked = false;
15742             this.setChecked(true, true);
15743         }
15744     },
15745
15746     // private
15747     destroy : function(){
15748         if(this.rendered){
15749             Roo.menu.MenuMgr.unregisterCheckable(this);
15750         }
15751         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15752     },
15753
15754     /**
15755      * Set the checked state of this item
15756      * @param {Boolean} checked The new checked value
15757      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15758      */
15759     setChecked : function(state, suppressEvent){
15760         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15761             if(this.container){
15762                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15763             }
15764             this.checked = state;
15765             if(suppressEvent !== true){
15766                 this.fireEvent("checkchange", this, state);
15767             }
15768         }
15769     },
15770
15771     // private
15772     handleClick : function(e){
15773        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15774            this.setChecked(!this.checked);
15775        }
15776        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15777     }
15778 });/*
15779  * Based on:
15780  * Ext JS Library 1.1.1
15781  * Copyright(c) 2006-2007, Ext JS, LLC.
15782  *
15783  * Originally Released Under LGPL - original licence link has changed is not relivant.
15784  *
15785  * Fork - LGPL
15786  * <script type="text/javascript">
15787  */
15788  
15789 /**
15790  * @class Roo.menu.DateItem
15791  * @extends Roo.menu.Adapter
15792  * A menu item that wraps the {@link Roo.DatPicker} component.
15793  * @constructor
15794  * Creates a new DateItem
15795  * @param {Object} config Configuration options
15796  */
15797 Roo.menu.DateItem = function(config){
15798     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15799     /** The Roo.DatePicker object @type Roo.DatePicker */
15800     this.picker = this.component;
15801     this.addEvents({select: true});
15802     
15803     this.picker.on("render", function(picker){
15804         picker.getEl().swallowEvent("click");
15805         picker.container.addClass("x-menu-date-item");
15806     });
15807
15808     this.picker.on("select", this.onSelect, this);
15809 };
15810
15811 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15812     // private
15813     onSelect : function(picker, date){
15814         this.fireEvent("select", this, date, picker);
15815         Roo.menu.DateItem.superclass.handleClick.call(this);
15816     }
15817 });/*
15818  * Based on:
15819  * Ext JS Library 1.1.1
15820  * Copyright(c) 2006-2007, Ext JS, LLC.
15821  *
15822  * Originally Released Under LGPL - original licence link has changed is not relivant.
15823  *
15824  * Fork - LGPL
15825  * <script type="text/javascript">
15826  */
15827  
15828 /**
15829  * @class Roo.menu.ColorItem
15830  * @extends Roo.menu.Adapter
15831  * A menu item that wraps the {@link Roo.ColorPalette} component.
15832  * @constructor
15833  * Creates a new ColorItem
15834  * @param {Object} config Configuration options
15835  */
15836 Roo.menu.ColorItem = function(config){
15837     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15838     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15839     this.palette = this.component;
15840     this.relayEvents(this.palette, ["select"]);
15841     if(this.selectHandler){
15842         this.on('select', this.selectHandler, this.scope);
15843     }
15844 };
15845 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15846  * Based on:
15847  * Ext JS Library 1.1.1
15848  * Copyright(c) 2006-2007, Ext JS, LLC.
15849  *
15850  * Originally Released Under LGPL - original licence link has changed is not relivant.
15851  *
15852  * Fork - LGPL
15853  * <script type="text/javascript">
15854  */
15855  
15856
15857 /**
15858  * @class Roo.menu.DateMenu
15859  * @extends Roo.menu.Menu
15860  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15861  * @constructor
15862  * Creates a new DateMenu
15863  * @param {Object} config Configuration options
15864  */
15865 Roo.menu.DateMenu = function(config){
15866     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15867     this.plain = true;
15868     var di = new Roo.menu.DateItem(config);
15869     this.add(di);
15870     /**
15871      * The {@link Roo.DatePicker} instance for this DateMenu
15872      * @type DatePicker
15873      */
15874     this.picker = di.picker;
15875     /**
15876      * @event select
15877      * @param {DatePicker} picker
15878      * @param {Date} date
15879      */
15880     this.relayEvents(di, ["select"]);
15881     this.on('beforeshow', function(){
15882         if(this.picker){
15883             this.picker.hideMonthPicker(false);
15884         }
15885     }, this);
15886 };
15887 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15888     cls:'x-date-menu'
15889 });/*
15890  * Based on:
15891  * Ext JS Library 1.1.1
15892  * Copyright(c) 2006-2007, Ext JS, LLC.
15893  *
15894  * Originally Released Under LGPL - original licence link has changed is not relivant.
15895  *
15896  * Fork - LGPL
15897  * <script type="text/javascript">
15898  */
15899  
15900
15901 /**
15902  * @class Roo.menu.ColorMenu
15903  * @extends Roo.menu.Menu
15904  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15905  * @constructor
15906  * Creates a new ColorMenu
15907  * @param {Object} config Configuration options
15908  */
15909 Roo.menu.ColorMenu = function(config){
15910     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15911     this.plain = true;
15912     var ci = new Roo.menu.ColorItem(config);
15913     this.add(ci);
15914     /**
15915      * The {@link Roo.ColorPalette} instance for this ColorMenu
15916      * @type ColorPalette
15917      */
15918     this.palette = ci.palette;
15919     /**
15920      * @event select
15921      * @param {ColorPalette} palette
15922      * @param {String} color
15923      */
15924     this.relayEvents(ci, ["select"]);
15925 };
15926 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15927  * Based on:
15928  * Ext JS Library 1.1.1
15929  * Copyright(c) 2006-2007, Ext JS, LLC.
15930  *
15931  * Originally Released Under LGPL - original licence link has changed is not relivant.
15932  *
15933  * Fork - LGPL
15934  * <script type="text/javascript">
15935  */
15936  
15937 /**
15938  * @class Roo.form.TextItem
15939  * @extends Roo.BoxComponent
15940  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15941  * @constructor
15942  * Creates a new TextItem
15943  * @param {Object} config Configuration options
15944  */
15945 Roo.form.TextItem = function(config){
15946     Roo.form.TextItem.superclass.constructor.call(this, config);
15947 };
15948
15949 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15950     
15951     /**
15952      * @cfg {String} tag the tag for this item (default div)
15953      */
15954     tag : 'div',
15955     /**
15956      * @cfg {String} html the content for this item
15957      */
15958     html : '',
15959     
15960     getAutoCreate : function()
15961     {
15962         var cfg = {
15963             id: this.id,
15964             tag: this.tag,
15965             html: this.html,
15966             cls: 'x-form-item'
15967         };
15968         
15969         return cfg;
15970         
15971     },
15972     
15973     onRender : function(ct, position)
15974     {
15975         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15976         
15977         if(!this.el){
15978             var cfg = this.getAutoCreate();
15979             if(!cfg.name){
15980                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15981             }
15982             if (!cfg.name.length) {
15983                 delete cfg.name;
15984             }
15985             this.el = ct.createChild(cfg, position);
15986         }
15987     },
15988     /*
15989      * setHTML
15990      * @param {String} html update the Contents of the element.
15991      */
15992     setHTML : function(html)
15993     {
15994         this.fieldEl.dom.innerHTML = html;
15995     }
15996     
15997 });/*
15998  * Based on:
15999  * Ext JS Library 1.1.1
16000  * Copyright(c) 2006-2007, Ext JS, LLC.
16001  *
16002  * Originally Released Under LGPL - original licence link has changed is not relivant.
16003  *
16004  * Fork - LGPL
16005  * <script type="text/javascript">
16006  */
16007  
16008 /**
16009  * @class Roo.form.Field
16010  * @extends Roo.BoxComponent
16011  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16012  * @constructor
16013  * Creates a new Field
16014  * @param {Object} config Configuration options
16015  */
16016 Roo.form.Field = function(config){
16017     Roo.form.Field.superclass.constructor.call(this, config);
16018 };
16019
16020 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16021     /**
16022      * @cfg {String} fieldLabel Label to use when rendering a form.
16023      */
16024        /**
16025      * @cfg {String} qtip Mouse over tip
16026      */
16027      
16028     /**
16029      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16030      */
16031     invalidClass : "x-form-invalid",
16032     /**
16033      * @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")
16034      */
16035     invalidText : "The value in this field is invalid",
16036     /**
16037      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16038      */
16039     focusClass : "x-form-focus",
16040     /**
16041      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16042       automatic validation (defaults to "keyup").
16043      */
16044     validationEvent : "keyup",
16045     /**
16046      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16047      */
16048     validateOnBlur : true,
16049     /**
16050      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16051      */
16052     validationDelay : 250,
16053     /**
16054      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16055      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16056      */
16057     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16058     /**
16059      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16060      */
16061     fieldClass : "x-form-field",
16062     /**
16063      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16064      *<pre>
16065 Value         Description
16066 -----------   ----------------------------------------------------------------------
16067 qtip          Display a quick tip when the user hovers over the field
16068 title         Display a default browser title attribute popup
16069 under         Add a block div beneath the field containing the error text
16070 side          Add an error icon to the right of the field with a popup on hover
16071 [element id]  Add the error text directly to the innerHTML of the specified element
16072 </pre>
16073      */
16074     msgTarget : 'qtip',
16075     /**
16076      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16077      */
16078     msgFx : 'normal',
16079
16080     /**
16081      * @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.
16082      */
16083     readOnly : false,
16084
16085     /**
16086      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16087      */
16088     disabled : false,
16089
16090     /**
16091      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16092      */
16093     inputType : undefined,
16094     
16095     /**
16096      * @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).
16097          */
16098         tabIndex : undefined,
16099         
16100     // private
16101     isFormField : true,
16102
16103     // private
16104     hasFocus : false,
16105     /**
16106      * @property {Roo.Element} fieldEl
16107      * Element Containing the rendered Field (with label etc.)
16108      */
16109     /**
16110      * @cfg {Mixed} value A value to initialize this field with.
16111      */
16112     value : undefined,
16113
16114     /**
16115      * @cfg {String} name The field's HTML name attribute.
16116      */
16117     /**
16118      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16119      */
16120     // private
16121     loadedValue : false,
16122      
16123      
16124         // private ??
16125         initComponent : function(){
16126         Roo.form.Field.superclass.initComponent.call(this);
16127         this.addEvents({
16128             /**
16129              * @event focus
16130              * Fires when this field receives input focus.
16131              * @param {Roo.form.Field} this
16132              */
16133             focus : true,
16134             /**
16135              * @event blur
16136              * Fires when this field loses input focus.
16137              * @param {Roo.form.Field} this
16138              */
16139             blur : true,
16140             /**
16141              * @event specialkey
16142              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16143              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16144              * @param {Roo.form.Field} this
16145              * @param {Roo.EventObject} e The event object
16146              */
16147             specialkey : true,
16148             /**
16149              * @event change
16150              * Fires just before the field blurs if the field value has changed.
16151              * @param {Roo.form.Field} this
16152              * @param {Mixed} newValue The new value
16153              * @param {Mixed} oldValue The original value
16154              */
16155             change : true,
16156             /**
16157              * @event invalid
16158              * Fires after the field has been marked as invalid.
16159              * @param {Roo.form.Field} this
16160              * @param {String} msg The validation message
16161              */
16162             invalid : true,
16163             /**
16164              * @event valid
16165              * Fires after the field has been validated with no errors.
16166              * @param {Roo.form.Field} this
16167              */
16168             valid : true,
16169              /**
16170              * @event keyup
16171              * Fires after the key up
16172              * @param {Roo.form.Field} this
16173              * @param {Roo.EventObject}  e The event Object
16174              */
16175             keyup : true
16176         });
16177     },
16178
16179     /**
16180      * Returns the name attribute of the field if available
16181      * @return {String} name The field name
16182      */
16183     getName: function(){
16184          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16185     },
16186
16187     // private
16188     onRender : function(ct, position){
16189         Roo.form.Field.superclass.onRender.call(this, ct, position);
16190         if(!this.el){
16191             var cfg = this.getAutoCreate();
16192             if(!cfg.name){
16193                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16194             }
16195             if (!cfg.name.length) {
16196                 delete cfg.name;
16197             }
16198             if(this.inputType){
16199                 cfg.type = this.inputType;
16200             }
16201             this.el = ct.createChild(cfg, position);
16202         }
16203         var type = this.el.dom.type;
16204         if(type){
16205             if(type == 'password'){
16206                 type = 'text';
16207             }
16208             this.el.addClass('x-form-'+type);
16209         }
16210         if(this.readOnly){
16211             this.el.dom.readOnly = true;
16212         }
16213         if(this.tabIndex !== undefined){
16214             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16215         }
16216
16217         this.el.addClass([this.fieldClass, this.cls]);
16218         this.initValue();
16219     },
16220
16221     /**
16222      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16223      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16224      * @return {Roo.form.Field} this
16225      */
16226     applyTo : function(target){
16227         this.allowDomMove = false;
16228         this.el = Roo.get(target);
16229         this.render(this.el.dom.parentNode);
16230         return this;
16231     },
16232
16233     // private
16234     initValue : function(){
16235         if(this.value !== undefined){
16236             this.setValue(this.value);
16237         }else if(this.el.dom.value.length > 0){
16238             this.setValue(this.el.dom.value);
16239         }
16240     },
16241
16242     /**
16243      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16244      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16245      */
16246     isDirty : function() {
16247         if(this.disabled) {
16248             return false;
16249         }
16250         return String(this.getValue()) !== String(this.originalValue);
16251     },
16252
16253     /**
16254      * stores the current value in loadedValue
16255      */
16256     resetHasChanged : function()
16257     {
16258         this.loadedValue = String(this.getValue());
16259     },
16260     /**
16261      * checks the current value against the 'loaded' value.
16262      * Note - will return false if 'resetHasChanged' has not been called first.
16263      */
16264     hasChanged : function()
16265     {
16266         if(this.disabled || this.readOnly) {
16267             return false;
16268         }
16269         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16270     },
16271     
16272     
16273     
16274     // private
16275     afterRender : function(){
16276         Roo.form.Field.superclass.afterRender.call(this);
16277         this.initEvents();
16278     },
16279
16280     // private
16281     fireKey : function(e){
16282         //Roo.log('field ' + e.getKey());
16283         if(e.isNavKeyPress()){
16284             this.fireEvent("specialkey", this, e);
16285         }
16286     },
16287
16288     /**
16289      * Resets the current field value to the originally loaded value and clears any validation messages
16290      */
16291     reset : function(){
16292         this.setValue(this.resetValue);
16293         this.originalValue = this.getValue();
16294         this.clearInvalid();
16295     },
16296
16297     // private
16298     initEvents : function(){
16299         // safari killled keypress - so keydown is now used..
16300         this.el.on("keydown" , this.fireKey,  this);
16301         this.el.on("focus", this.onFocus,  this);
16302         this.el.on("blur", this.onBlur,  this);
16303         this.el.relayEvent('keyup', this);
16304
16305         // reference to original value for reset
16306         this.originalValue = this.getValue();
16307         this.resetValue =  this.getValue();
16308     },
16309
16310     // private
16311     onFocus : function(){
16312         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16313             this.el.addClass(this.focusClass);
16314         }
16315         if(!this.hasFocus){
16316             this.hasFocus = true;
16317             this.startValue = this.getValue();
16318             this.fireEvent("focus", this);
16319         }
16320     },
16321
16322     beforeBlur : Roo.emptyFn,
16323
16324     // private
16325     onBlur : function(){
16326         this.beforeBlur();
16327         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16328             this.el.removeClass(this.focusClass);
16329         }
16330         this.hasFocus = false;
16331         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16332             this.validate();
16333         }
16334         var v = this.getValue();
16335         if(String(v) !== String(this.startValue)){
16336             this.fireEvent('change', this, v, this.startValue);
16337         }
16338         this.fireEvent("blur", this);
16339     },
16340
16341     /**
16342      * Returns whether or not the field value is currently valid
16343      * @param {Boolean} preventMark True to disable marking the field invalid
16344      * @return {Boolean} True if the value is valid, else false
16345      */
16346     isValid : function(preventMark){
16347         if(this.disabled){
16348             return true;
16349         }
16350         var restore = this.preventMark;
16351         this.preventMark = preventMark === true;
16352         var v = this.validateValue(this.processValue(this.getRawValue()));
16353         this.preventMark = restore;
16354         return v;
16355     },
16356
16357     /**
16358      * Validates the field value
16359      * @return {Boolean} True if the value is valid, else false
16360      */
16361     validate : function(){
16362         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16363             this.clearInvalid();
16364             return true;
16365         }
16366         return false;
16367     },
16368
16369     processValue : function(value){
16370         return value;
16371     },
16372
16373     // private
16374     // Subclasses should provide the validation implementation by overriding this
16375     validateValue : function(value){
16376         return true;
16377     },
16378
16379     /**
16380      * Mark this field as invalid
16381      * @param {String} msg The validation message
16382      */
16383     markInvalid : function(msg){
16384         if(!this.rendered || this.preventMark){ // not rendered
16385             return;
16386         }
16387         
16388         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16389         
16390         obj.el.addClass(this.invalidClass);
16391         msg = msg || this.invalidText;
16392         switch(this.msgTarget){
16393             case 'qtip':
16394                 obj.el.dom.qtip = msg;
16395                 obj.el.dom.qclass = 'x-form-invalid-tip';
16396                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16397                     Roo.QuickTips.enable();
16398                 }
16399                 break;
16400             case 'title':
16401                 this.el.dom.title = msg;
16402                 break;
16403             case 'under':
16404                 if(!this.errorEl){
16405                     var elp = this.el.findParent('.x-form-element', 5, true);
16406                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16407                     this.errorEl.setWidth(elp.getWidth(true)-20);
16408                 }
16409                 this.errorEl.update(msg);
16410                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16411                 break;
16412             case 'side':
16413                 if(!this.errorIcon){
16414                     var elp = this.el.findParent('.x-form-element', 5, true);
16415                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16416                 }
16417                 this.alignErrorIcon();
16418                 this.errorIcon.dom.qtip = msg;
16419                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16420                 this.errorIcon.show();
16421                 this.on('resize', this.alignErrorIcon, this);
16422                 break;
16423             default:
16424                 var t = Roo.getDom(this.msgTarget);
16425                 t.innerHTML = msg;
16426                 t.style.display = this.msgDisplay;
16427                 break;
16428         }
16429         this.fireEvent('invalid', this, msg);
16430     },
16431
16432     // private
16433     alignErrorIcon : function(){
16434         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16435     },
16436
16437     /**
16438      * Clear any invalid styles/messages for this field
16439      */
16440     clearInvalid : function(){
16441         if(!this.rendered || this.preventMark){ // not rendered
16442             return;
16443         }
16444         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16445         
16446         obj.el.removeClass(this.invalidClass);
16447         switch(this.msgTarget){
16448             case 'qtip':
16449                 obj.el.dom.qtip = '';
16450                 break;
16451             case 'title':
16452                 this.el.dom.title = '';
16453                 break;
16454             case 'under':
16455                 if(this.errorEl){
16456                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16457                 }
16458                 break;
16459             case 'side':
16460                 if(this.errorIcon){
16461                     this.errorIcon.dom.qtip = '';
16462                     this.errorIcon.hide();
16463                     this.un('resize', this.alignErrorIcon, this);
16464                 }
16465                 break;
16466             default:
16467                 var t = Roo.getDom(this.msgTarget);
16468                 t.innerHTML = '';
16469                 t.style.display = 'none';
16470                 break;
16471         }
16472         this.fireEvent('valid', this);
16473     },
16474
16475     /**
16476      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16477      * @return {Mixed} value The field value
16478      */
16479     getRawValue : function(){
16480         var v = this.el.getValue();
16481         
16482         return v;
16483     },
16484
16485     /**
16486      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16487      * @return {Mixed} value The field value
16488      */
16489     getValue : function(){
16490         var v = this.el.getValue();
16491          
16492         return v;
16493     },
16494
16495     /**
16496      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16497      * @param {Mixed} value The value to set
16498      */
16499     setRawValue : function(v){
16500         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16501     },
16502
16503     /**
16504      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16505      * @param {Mixed} value The value to set
16506      */
16507     setValue : function(v){
16508         this.value = v;
16509         if(this.rendered){
16510             this.el.dom.value = (v === null || v === undefined ? '' : v);
16511              this.validate();
16512         }
16513     },
16514
16515     adjustSize : function(w, h){
16516         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16517         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16518         return s;
16519     },
16520
16521     adjustWidth : function(tag, w){
16522         tag = tag.toLowerCase();
16523         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16524             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16525                 if(tag == 'input'){
16526                     return w + 2;
16527                 }
16528                 if(tag == 'textarea'){
16529                     return w-2;
16530                 }
16531             }else if(Roo.isOpera){
16532                 if(tag == 'input'){
16533                     return w + 2;
16534                 }
16535                 if(tag == 'textarea'){
16536                     return w-2;
16537                 }
16538             }
16539         }
16540         return w;
16541     }
16542 });
16543
16544
16545 // anything other than normal should be considered experimental
16546 Roo.form.Field.msgFx = {
16547     normal : {
16548         show: function(msgEl, f){
16549             msgEl.setDisplayed('block');
16550         },
16551
16552         hide : function(msgEl, f){
16553             msgEl.setDisplayed(false).update('');
16554         }
16555     },
16556
16557     slide : {
16558         show: function(msgEl, f){
16559             msgEl.slideIn('t', {stopFx:true});
16560         },
16561
16562         hide : function(msgEl, f){
16563             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16564         }
16565     },
16566
16567     slideRight : {
16568         show: function(msgEl, f){
16569             msgEl.fixDisplay();
16570             msgEl.alignTo(f.el, 'tl-tr');
16571             msgEl.slideIn('l', {stopFx:true});
16572         },
16573
16574         hide : function(msgEl, f){
16575             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16576         }
16577     }
16578 };/*
16579  * Based on:
16580  * Ext JS Library 1.1.1
16581  * Copyright(c) 2006-2007, Ext JS, LLC.
16582  *
16583  * Originally Released Under LGPL - original licence link has changed is not relivant.
16584  *
16585  * Fork - LGPL
16586  * <script type="text/javascript">
16587  */
16588  
16589
16590 /**
16591  * @class Roo.form.TextField
16592  * @extends Roo.form.Field
16593  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16594  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16595  * @constructor
16596  * Creates a new TextField
16597  * @param {Object} config Configuration options
16598  */
16599 Roo.form.TextField = function(config){
16600     Roo.form.TextField.superclass.constructor.call(this, config);
16601     this.addEvents({
16602         /**
16603          * @event autosize
16604          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16605          * according to the default logic, but this event provides a hook for the developer to apply additional
16606          * logic at runtime to resize the field if needed.
16607              * @param {Roo.form.Field} this This text field
16608              * @param {Number} width The new field width
16609              */
16610         autosize : true
16611     });
16612 };
16613
16614 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16615     /**
16616      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16617      */
16618     grow : false,
16619     /**
16620      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16621      */
16622     growMin : 30,
16623     /**
16624      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16625      */
16626     growMax : 800,
16627     /**
16628      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16629      */
16630     vtype : null,
16631     /**
16632      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16633      */
16634     maskRe : null,
16635     /**
16636      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16637      */
16638     disableKeyFilter : false,
16639     /**
16640      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16641      */
16642     allowBlank : true,
16643     /**
16644      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16645      */
16646     minLength : 0,
16647     /**
16648      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16649      */
16650     maxLength : Number.MAX_VALUE,
16651     /**
16652      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16653      */
16654     minLengthText : "The minimum length for this field is {0}",
16655     /**
16656      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16657      */
16658     maxLengthText : "The maximum length for this field is {0}",
16659     /**
16660      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16661      */
16662     selectOnFocus : false,
16663     /**
16664      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16665      */    
16666     allowLeadingSpace : false,
16667     /**
16668      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16669      */
16670     blankText : "This field is required",
16671     /**
16672      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16673      * If available, this function will be called only after the basic validators all return true, and will be passed the
16674      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16675      */
16676     validator : null,
16677     /**
16678      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16679      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16680      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16681      */
16682     regex : null,
16683     /**
16684      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16685      */
16686     regexText : "",
16687     /**
16688      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16689      */
16690     emptyText : null,
16691    
16692
16693     // private
16694     initEvents : function()
16695     {
16696         if (this.emptyText) {
16697             this.el.attr('placeholder', this.emptyText);
16698         }
16699         
16700         Roo.form.TextField.superclass.initEvents.call(this);
16701         if(this.validationEvent == 'keyup'){
16702             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16703             this.el.on('keyup', this.filterValidation, this);
16704         }
16705         else if(this.validationEvent !== false){
16706             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16707         }
16708         
16709         if(this.selectOnFocus){
16710             this.on("focus", this.preFocus, this);
16711         }
16712         if (!this.allowLeadingSpace) {
16713             this.on('blur', this.cleanLeadingSpace, this);
16714         }
16715         
16716         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16717             this.el.on("keypress", this.filterKeys, this);
16718         }
16719         if(this.grow){
16720             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16721             this.el.on("click", this.autoSize,  this);
16722         }
16723         if(this.el.is('input[type=password]') && Roo.isSafari){
16724             this.el.on('keydown', this.SafariOnKeyDown, this);
16725         }
16726     },
16727
16728     processValue : function(value){
16729         if(this.stripCharsRe){
16730             var newValue = value.replace(this.stripCharsRe, '');
16731             if(newValue !== value){
16732                 this.setRawValue(newValue);
16733                 return newValue;
16734             }
16735         }
16736         return value;
16737     },
16738
16739     filterValidation : function(e){
16740         if(!e.isNavKeyPress()){
16741             this.validationTask.delay(this.validationDelay);
16742         }
16743     },
16744
16745     // private
16746     onKeyUp : function(e){
16747         if(!e.isNavKeyPress()){
16748             this.autoSize();
16749         }
16750     },
16751     // private - clean the leading white space
16752     cleanLeadingSpace : function(e)
16753     {
16754         if ( this.inputType == 'file') {
16755             return;
16756         }
16757         
16758         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16759     },
16760     /**
16761      * Resets the current field value to the originally-loaded value and clears any validation messages.
16762      *  
16763      */
16764     reset : function(){
16765         Roo.form.TextField.superclass.reset.call(this);
16766        
16767     }, 
16768     // private
16769     preFocus : function(){
16770         
16771         if(this.selectOnFocus){
16772             this.el.dom.select();
16773         }
16774     },
16775
16776     
16777     // private
16778     filterKeys : function(e){
16779         var k = e.getKey();
16780         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16781             return;
16782         }
16783         var c = e.getCharCode(), cc = String.fromCharCode(c);
16784         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16785             return;
16786         }
16787         if(!this.maskRe.test(cc)){
16788             e.stopEvent();
16789         }
16790     },
16791
16792     setValue : function(v){
16793         
16794         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16795         
16796         this.autoSize();
16797     },
16798
16799     /**
16800      * Validates a value according to the field's validation rules and marks the field as invalid
16801      * if the validation fails
16802      * @param {Mixed} value The value to validate
16803      * @return {Boolean} True if the value is valid, else false
16804      */
16805     validateValue : function(value){
16806         if(value.length < 1)  { // if it's blank
16807              if(this.allowBlank){
16808                 this.clearInvalid();
16809                 return true;
16810              }else{
16811                 this.markInvalid(this.blankText);
16812                 return false;
16813              }
16814         }
16815         if(value.length < this.minLength){
16816             this.markInvalid(String.format(this.minLengthText, this.minLength));
16817             return false;
16818         }
16819         if(value.length > this.maxLength){
16820             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16821             return false;
16822         }
16823         if(this.vtype){
16824             var vt = Roo.form.VTypes;
16825             if(!vt[this.vtype](value, this)){
16826                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16827                 return false;
16828             }
16829         }
16830         if(typeof this.validator == "function"){
16831             var msg = this.validator(value);
16832             if(msg !== true){
16833                 this.markInvalid(msg);
16834                 return false;
16835             }
16836         }
16837         if(this.regex && !this.regex.test(value)){
16838             this.markInvalid(this.regexText);
16839             return false;
16840         }
16841         return true;
16842     },
16843
16844     /**
16845      * Selects text in this field
16846      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16847      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16848      */
16849     selectText : function(start, end){
16850         var v = this.getRawValue();
16851         if(v.length > 0){
16852             start = start === undefined ? 0 : start;
16853             end = end === undefined ? v.length : end;
16854             var d = this.el.dom;
16855             if(d.setSelectionRange){
16856                 d.setSelectionRange(start, end);
16857             }else if(d.createTextRange){
16858                 var range = d.createTextRange();
16859                 range.moveStart("character", start);
16860                 range.moveEnd("character", v.length-end);
16861                 range.select();
16862             }
16863         }
16864     },
16865
16866     /**
16867      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16868      * This only takes effect if grow = true, and fires the autosize event.
16869      */
16870     autoSize : function(){
16871         if(!this.grow || !this.rendered){
16872             return;
16873         }
16874         if(!this.metrics){
16875             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16876         }
16877         var el = this.el;
16878         var v = el.dom.value;
16879         var d = document.createElement('div');
16880         d.appendChild(document.createTextNode(v));
16881         v = d.innerHTML;
16882         d = null;
16883         v += "&#160;";
16884         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16885         this.el.setWidth(w);
16886         this.fireEvent("autosize", this, w);
16887     },
16888     
16889     // private
16890     SafariOnKeyDown : function(event)
16891     {
16892         // this is a workaround for a password hang bug on chrome/ webkit.
16893         
16894         var isSelectAll = false;
16895         
16896         if(this.el.dom.selectionEnd > 0){
16897             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16898         }
16899         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16900             event.preventDefault();
16901             this.setValue('');
16902             return;
16903         }
16904         
16905         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16906             
16907             event.preventDefault();
16908             // this is very hacky as keydown always get's upper case.
16909             
16910             var cc = String.fromCharCode(event.getCharCode());
16911             
16912             
16913             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16914             
16915         }
16916         
16917         
16918     }
16919 });/*
16920  * Based on:
16921  * Ext JS Library 1.1.1
16922  * Copyright(c) 2006-2007, Ext JS, LLC.
16923  *
16924  * Originally Released Under LGPL - original licence link has changed is not relivant.
16925  *
16926  * Fork - LGPL
16927  * <script type="text/javascript">
16928  */
16929  
16930 /**
16931  * @class Roo.form.Hidden
16932  * @extends Roo.form.TextField
16933  * Simple Hidden element used on forms 
16934  * 
16935  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16936  * 
16937  * @constructor
16938  * Creates a new Hidden form element.
16939  * @param {Object} config Configuration options
16940  */
16941
16942
16943
16944 // easy hidden field...
16945 Roo.form.Hidden = function(config){
16946     Roo.form.Hidden.superclass.constructor.call(this, config);
16947 };
16948   
16949 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16950     fieldLabel:      '',
16951     inputType:      'hidden',
16952     width:          50,
16953     allowBlank:     true,
16954     labelSeparator: '',
16955     hidden:         true,
16956     itemCls :       'x-form-item-display-none'
16957
16958
16959 });
16960
16961
16962 /*
16963  * Based on:
16964  * Ext JS Library 1.1.1
16965  * Copyright(c) 2006-2007, Ext JS, LLC.
16966  *
16967  * Originally Released Under LGPL - original licence link has changed is not relivant.
16968  *
16969  * Fork - LGPL
16970  * <script type="text/javascript">
16971  */
16972  
16973 /**
16974  * @class Roo.form.TriggerField
16975  * @extends Roo.form.TextField
16976  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16977  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16978  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16979  * for which you can provide a custom implementation.  For example:
16980  * <pre><code>
16981 var trigger = new Roo.form.TriggerField();
16982 trigger.onTriggerClick = myTriggerFn;
16983 trigger.applyTo('my-field');
16984 </code></pre>
16985  *
16986  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16987  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16988  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16989  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16990  * @constructor
16991  * Create a new TriggerField.
16992  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16993  * to the base TextField)
16994  */
16995 Roo.form.TriggerField = function(config){
16996     this.mimicing = false;
16997     Roo.form.TriggerField.superclass.constructor.call(this, config);
16998 };
16999
17000 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17001     /**
17002      * @cfg {String} triggerClass A CSS class to apply to the trigger
17003      */
17004     /**
17005      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17006      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17007      */
17008     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17009     /**
17010      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17011      */
17012     hideTrigger:false,
17013
17014     /** @cfg {Boolean} grow @hide */
17015     /** @cfg {Number} growMin @hide */
17016     /** @cfg {Number} growMax @hide */
17017
17018     /**
17019      * @hide 
17020      * @method
17021      */
17022     autoSize: Roo.emptyFn,
17023     // private
17024     monitorTab : true,
17025     // private
17026     deferHeight : true,
17027
17028     
17029     actionMode : 'wrap',
17030     // private
17031     onResize : function(w, h){
17032         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17033         if(typeof w == 'number'){
17034             var x = w - this.trigger.getWidth();
17035             this.el.setWidth(this.adjustWidth('input', x));
17036             this.trigger.setStyle('left', x+'px');
17037         }
17038     },
17039
17040     // private
17041     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17042
17043     // private
17044     getResizeEl : function(){
17045         return this.wrap;
17046     },
17047
17048     // private
17049     getPositionEl : function(){
17050         return this.wrap;
17051     },
17052
17053     // private
17054     alignErrorIcon : function(){
17055         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17056     },
17057
17058     // private
17059     onRender : function(ct, position){
17060         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17061         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17062         this.trigger = this.wrap.createChild(this.triggerConfig ||
17063                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17064         if(this.hideTrigger){
17065             this.trigger.setDisplayed(false);
17066         }
17067         this.initTrigger();
17068         if(!this.width){
17069             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17070         }
17071     },
17072
17073     // private
17074     initTrigger : function(){
17075         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17076         this.trigger.addClassOnOver('x-form-trigger-over');
17077         this.trigger.addClassOnClick('x-form-trigger-click');
17078     },
17079
17080     // private
17081     onDestroy : function(){
17082         if(this.trigger){
17083             this.trigger.removeAllListeners();
17084             this.trigger.remove();
17085         }
17086         if(this.wrap){
17087             this.wrap.remove();
17088         }
17089         Roo.form.TriggerField.superclass.onDestroy.call(this);
17090     },
17091
17092     // private
17093     onFocus : function(){
17094         Roo.form.TriggerField.superclass.onFocus.call(this);
17095         if(!this.mimicing){
17096             this.wrap.addClass('x-trigger-wrap-focus');
17097             this.mimicing = true;
17098             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17099             if(this.monitorTab){
17100                 this.el.on("keydown", this.checkTab, this);
17101             }
17102         }
17103     },
17104
17105     // private
17106     checkTab : function(e){
17107         if(e.getKey() == e.TAB){
17108             this.triggerBlur();
17109         }
17110     },
17111
17112     // private
17113     onBlur : function(){
17114         // do nothing
17115     },
17116
17117     // private
17118     mimicBlur : function(e, t){
17119         if(!this.wrap.contains(t) && this.validateBlur()){
17120             this.triggerBlur();
17121         }
17122     },
17123
17124     // private
17125     triggerBlur : function(){
17126         this.mimicing = false;
17127         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17128         if(this.monitorTab){
17129             this.el.un("keydown", this.checkTab, this);
17130         }
17131         this.wrap.removeClass('x-trigger-wrap-focus');
17132         Roo.form.TriggerField.superclass.onBlur.call(this);
17133     },
17134
17135     // private
17136     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17137     validateBlur : function(e, t){
17138         return true;
17139     },
17140
17141     // private
17142     onDisable : function(){
17143         Roo.form.TriggerField.superclass.onDisable.call(this);
17144         if(this.wrap){
17145             this.wrap.addClass('x-item-disabled');
17146         }
17147     },
17148
17149     // private
17150     onEnable : function(){
17151         Roo.form.TriggerField.superclass.onEnable.call(this);
17152         if(this.wrap){
17153             this.wrap.removeClass('x-item-disabled');
17154         }
17155     },
17156
17157     // private
17158     onShow : function(){
17159         var ae = this.getActionEl();
17160         
17161         if(ae){
17162             ae.dom.style.display = '';
17163             ae.dom.style.visibility = 'visible';
17164         }
17165     },
17166
17167     // private
17168     
17169     onHide : function(){
17170         var ae = this.getActionEl();
17171         ae.dom.style.display = 'none';
17172     },
17173
17174     /**
17175      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17176      * by an implementing function.
17177      * @method
17178      * @param {EventObject} e
17179      */
17180     onTriggerClick : Roo.emptyFn
17181 });
17182
17183 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17184 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17185 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17186 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17187     initComponent : function(){
17188         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17189
17190         this.triggerConfig = {
17191             tag:'span', cls:'x-form-twin-triggers', cn:[
17192             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17193             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17194         ]};
17195     },
17196
17197     getTrigger : function(index){
17198         return this.triggers[index];
17199     },
17200
17201     initTrigger : function(){
17202         var ts = this.trigger.select('.x-form-trigger', true);
17203         this.wrap.setStyle('overflow', 'hidden');
17204         var triggerField = this;
17205         ts.each(function(t, all, index){
17206             t.hide = function(){
17207                 var w = triggerField.wrap.getWidth();
17208                 this.dom.style.display = 'none';
17209                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17210             };
17211             t.show = function(){
17212                 var w = triggerField.wrap.getWidth();
17213                 this.dom.style.display = '';
17214                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17215             };
17216             var triggerIndex = 'Trigger'+(index+1);
17217
17218             if(this['hide'+triggerIndex]){
17219                 t.dom.style.display = 'none';
17220             }
17221             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17222             t.addClassOnOver('x-form-trigger-over');
17223             t.addClassOnClick('x-form-trigger-click');
17224         }, this);
17225         this.triggers = ts.elements;
17226     },
17227
17228     onTrigger1Click : Roo.emptyFn,
17229     onTrigger2Click : Roo.emptyFn
17230 });/*
17231  * Based on:
17232  * Ext JS Library 1.1.1
17233  * Copyright(c) 2006-2007, Ext JS, LLC.
17234  *
17235  * Originally Released Under LGPL - original licence link has changed is not relivant.
17236  *
17237  * Fork - LGPL
17238  * <script type="text/javascript">
17239  */
17240  
17241 /**
17242  * @class Roo.form.TextArea
17243  * @extends Roo.form.TextField
17244  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17245  * support for auto-sizing.
17246  * @constructor
17247  * Creates a new TextArea
17248  * @param {Object} config Configuration options
17249  */
17250 Roo.form.TextArea = function(config){
17251     Roo.form.TextArea.superclass.constructor.call(this, config);
17252     // these are provided exchanges for backwards compat
17253     // minHeight/maxHeight were replaced by growMin/growMax to be
17254     // compatible with TextField growing config values
17255     if(this.minHeight !== undefined){
17256         this.growMin = this.minHeight;
17257     }
17258     if(this.maxHeight !== undefined){
17259         this.growMax = this.maxHeight;
17260     }
17261 };
17262
17263 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17264     /**
17265      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17266      */
17267     growMin : 60,
17268     /**
17269      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17270      */
17271     growMax: 1000,
17272     /**
17273      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17274      * in the field (equivalent to setting overflow: hidden, defaults to false)
17275      */
17276     preventScrollbars: false,
17277     /**
17278      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17279      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17280      */
17281
17282     // private
17283     onRender : function(ct, position){
17284         if(!this.el){
17285             this.defaultAutoCreate = {
17286                 tag: "textarea",
17287                 style:"width:300px;height:60px;",
17288                 autocomplete: "new-password"
17289             };
17290         }
17291         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17292         if(this.grow){
17293             this.textSizeEl = Roo.DomHelper.append(document.body, {
17294                 tag: "pre", cls: "x-form-grow-sizer"
17295             });
17296             if(this.preventScrollbars){
17297                 this.el.setStyle("overflow", "hidden");
17298             }
17299             this.el.setHeight(this.growMin);
17300         }
17301     },
17302
17303     onDestroy : function(){
17304         if(this.textSizeEl){
17305             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17306         }
17307         Roo.form.TextArea.superclass.onDestroy.call(this);
17308     },
17309
17310     // private
17311     onKeyUp : function(e){
17312         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17313             this.autoSize();
17314         }
17315     },
17316
17317     /**
17318      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17319      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17320      */
17321     autoSize : function(){
17322         if(!this.grow || !this.textSizeEl){
17323             return;
17324         }
17325         var el = this.el;
17326         var v = el.dom.value;
17327         var ts = this.textSizeEl;
17328
17329         ts.innerHTML = '';
17330         ts.appendChild(document.createTextNode(v));
17331         v = ts.innerHTML;
17332
17333         Roo.fly(ts).setWidth(this.el.getWidth());
17334         if(v.length < 1){
17335             v = "&#160;&#160;";
17336         }else{
17337             if(Roo.isIE){
17338                 v = v.replace(/\n/g, '<p>&#160;</p>');
17339             }
17340             v += "&#160;\n&#160;";
17341         }
17342         ts.innerHTML = v;
17343         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17344         if(h != this.lastHeight){
17345             this.lastHeight = h;
17346             this.el.setHeight(h);
17347             this.fireEvent("autosize", this, h);
17348         }
17349     }
17350 });/*
17351  * Based on:
17352  * Ext JS Library 1.1.1
17353  * Copyright(c) 2006-2007, Ext JS, LLC.
17354  *
17355  * Originally Released Under LGPL - original licence link has changed is not relivant.
17356  *
17357  * Fork - LGPL
17358  * <script type="text/javascript">
17359  */
17360  
17361
17362 /**
17363  * @class Roo.form.NumberField
17364  * @extends Roo.form.TextField
17365  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17366  * @constructor
17367  * Creates a new NumberField
17368  * @param {Object} config Configuration options
17369  */
17370 Roo.form.NumberField = function(config){
17371     Roo.form.NumberField.superclass.constructor.call(this, config);
17372 };
17373
17374 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17375     /**
17376      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17377      */
17378     fieldClass: "x-form-field x-form-num-field",
17379     /**
17380      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17381      */
17382     allowDecimals : true,
17383     /**
17384      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17385      */
17386     decimalSeparator : ".",
17387     /**
17388      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17389      */
17390     decimalPrecision : 2,
17391     /**
17392      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17393      */
17394     allowNegative : true,
17395     /**
17396      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17397      */
17398     minValue : Number.NEGATIVE_INFINITY,
17399     /**
17400      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17401      */
17402     maxValue : Number.MAX_VALUE,
17403     /**
17404      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17405      */
17406     minText : "The minimum value for this field is {0}",
17407     /**
17408      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17409      */
17410     maxText : "The maximum value for this field is {0}",
17411     /**
17412      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17413      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17414      */
17415     nanText : "{0} is not a valid number",
17416
17417     // private
17418     initEvents : function(){
17419         Roo.form.NumberField.superclass.initEvents.call(this);
17420         var allowed = "0123456789";
17421         if(this.allowDecimals){
17422             allowed += this.decimalSeparator;
17423         }
17424         if(this.allowNegative){
17425             allowed += "-";
17426         }
17427         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17428         var keyPress = function(e){
17429             var k = e.getKey();
17430             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17431                 return;
17432             }
17433             var c = e.getCharCode();
17434             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17435                 e.stopEvent();
17436             }
17437         };
17438         this.el.on("keypress", keyPress, this);
17439     },
17440
17441     // private
17442     validateValue : function(value){
17443         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17444             return false;
17445         }
17446         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17447              return true;
17448         }
17449         var num = this.parseValue(value);
17450         if(isNaN(num)){
17451             this.markInvalid(String.format(this.nanText, value));
17452             return false;
17453         }
17454         if(num < this.minValue){
17455             this.markInvalid(String.format(this.minText, this.minValue));
17456             return false;
17457         }
17458         if(num > this.maxValue){
17459             this.markInvalid(String.format(this.maxText, this.maxValue));
17460             return false;
17461         }
17462         return true;
17463     },
17464
17465     getValue : function(){
17466         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17467     },
17468
17469     // private
17470     parseValue : function(value){
17471         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17472         return isNaN(value) ? '' : value;
17473     },
17474
17475     // private
17476     fixPrecision : function(value){
17477         var nan = isNaN(value);
17478         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17479             return nan ? '' : value;
17480         }
17481         return parseFloat(value).toFixed(this.decimalPrecision);
17482     },
17483
17484     setValue : function(v){
17485         v = this.fixPrecision(v);
17486         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17487     },
17488
17489     // private
17490     decimalPrecisionFcn : function(v){
17491         return Math.floor(v);
17492     },
17493
17494     beforeBlur : function(){
17495         var v = this.parseValue(this.getRawValue());
17496         if(v){
17497             this.setValue(v);
17498         }
17499     }
17500 });/*
17501  * Based on:
17502  * Ext JS Library 1.1.1
17503  * Copyright(c) 2006-2007, Ext JS, LLC.
17504  *
17505  * Originally Released Under LGPL - original licence link has changed is not relivant.
17506  *
17507  * Fork - LGPL
17508  * <script type="text/javascript">
17509  */
17510  
17511 /**
17512  * @class Roo.form.DateField
17513  * @extends Roo.form.TriggerField
17514  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17515 * @constructor
17516 * Create a new DateField
17517 * @param {Object} config
17518  */
17519 Roo.form.DateField = function(config)
17520 {
17521     Roo.form.DateField.superclass.constructor.call(this, config);
17522     
17523       this.addEvents({
17524          
17525         /**
17526          * @event select
17527          * Fires when a date is selected
17528              * @param {Roo.form.DateField} combo This combo box
17529              * @param {Date} date The date selected
17530              */
17531         'select' : true
17532          
17533     });
17534     
17535     
17536     if(typeof this.minValue == "string") {
17537         this.minValue = this.parseDate(this.minValue);
17538     }
17539     if(typeof this.maxValue == "string") {
17540         this.maxValue = this.parseDate(this.maxValue);
17541     }
17542     this.ddMatch = null;
17543     if(this.disabledDates){
17544         var dd = this.disabledDates;
17545         var re = "(?:";
17546         for(var i = 0; i < dd.length; i++){
17547             re += dd[i];
17548             if(i != dd.length-1) {
17549                 re += "|";
17550             }
17551         }
17552         this.ddMatch = new RegExp(re + ")");
17553     }
17554 };
17555
17556 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17557     /**
17558      * @cfg {String} format
17559      * The default date format string which can be overriden for localization support.  The format must be
17560      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17561      */
17562     format : "m/d/y",
17563     /**
17564      * @cfg {String} altFormats
17565      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17566      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17567      */
17568     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17569     /**
17570      * @cfg {Array} disabledDays
17571      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17572      */
17573     disabledDays : null,
17574     /**
17575      * @cfg {String} disabledDaysText
17576      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17577      */
17578     disabledDaysText : "Disabled",
17579     /**
17580      * @cfg {Array} disabledDates
17581      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17582      * expression so they are very powerful. Some examples:
17583      * <ul>
17584      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17585      * <li>["03/08", "09/16"] would disable those days for every year</li>
17586      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17587      * <li>["03/../2006"] would disable every day in March 2006</li>
17588      * <li>["^03"] would disable every day in every March</li>
17589      * </ul>
17590      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17591      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17592      */
17593     disabledDates : null,
17594     /**
17595      * @cfg {String} disabledDatesText
17596      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17597      */
17598     disabledDatesText : "Disabled",
17599         
17600         
17601         /**
17602      * @cfg {Date/String} zeroValue
17603      * if the date is less that this number, then the field is rendered as empty
17604      * default is 1800
17605      */
17606         zeroValue : '1800-01-01',
17607         
17608         
17609     /**
17610      * @cfg {Date/String} minValue
17611      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17612      * valid format (defaults to null).
17613      */
17614     minValue : null,
17615     /**
17616      * @cfg {Date/String} maxValue
17617      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17618      * valid format (defaults to null).
17619      */
17620     maxValue : null,
17621     /**
17622      * @cfg {String} minText
17623      * The error text to display when the date in the cell is before minValue (defaults to
17624      * 'The date in this field must be after {minValue}').
17625      */
17626     minText : "The date in this field must be equal to or after {0}",
17627     /**
17628      * @cfg {String} maxText
17629      * The error text to display when the date in the cell is after maxValue (defaults to
17630      * 'The date in this field must be before {maxValue}').
17631      */
17632     maxText : "The date in this field must be equal to or before {0}",
17633     /**
17634      * @cfg {String} invalidText
17635      * The error text to display when the date in the field is invalid (defaults to
17636      * '{value} is not a valid date - it must be in the format {format}').
17637      */
17638     invalidText : "{0} is not a valid date - it must be in the format {1}",
17639     /**
17640      * @cfg {String} triggerClass
17641      * An additional CSS class used to style the trigger button.  The trigger will always get the
17642      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17643      * which displays a calendar icon).
17644      */
17645     triggerClass : 'x-form-date-trigger',
17646     
17647
17648     /**
17649      * @cfg {Boolean} useIso
17650      * if enabled, then the date field will use a hidden field to store the 
17651      * real value as iso formated date. default (false)
17652      */ 
17653     useIso : false,
17654     /**
17655      * @cfg {String/Object} autoCreate
17656      * A DomHelper element spec, or true for a default element spec (defaults to
17657      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17658      */ 
17659     // private
17660     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17661     
17662     // private
17663     hiddenField: false,
17664     
17665     onRender : function(ct, position)
17666     {
17667         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17668         if (this.useIso) {
17669             //this.el.dom.removeAttribute('name'); 
17670             Roo.log("Changing name?");
17671             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17672             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17673                     'before', true);
17674             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17675             // prevent input submission
17676             this.hiddenName = this.name;
17677         }
17678             
17679             
17680     },
17681     
17682     // private
17683     validateValue : function(value)
17684     {
17685         value = this.formatDate(value);
17686         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17687             Roo.log('super failed');
17688             return false;
17689         }
17690         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17691              return true;
17692         }
17693         var svalue = value;
17694         value = this.parseDate(value);
17695         if(!value){
17696             Roo.log('parse date failed' + svalue);
17697             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17698             return false;
17699         }
17700         var time = value.getTime();
17701         if(this.minValue && time < this.minValue.getTime()){
17702             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17703             return false;
17704         }
17705         if(this.maxValue && time > this.maxValue.getTime()){
17706             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17707             return false;
17708         }
17709         if(this.disabledDays){
17710             var day = value.getDay();
17711             for(var i = 0; i < this.disabledDays.length; i++) {
17712                 if(day === this.disabledDays[i]){
17713                     this.markInvalid(this.disabledDaysText);
17714                     return false;
17715                 }
17716             }
17717         }
17718         var fvalue = this.formatDate(value);
17719         if(this.ddMatch && this.ddMatch.test(fvalue)){
17720             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17721             return false;
17722         }
17723         return true;
17724     },
17725
17726     // private
17727     // Provides logic to override the default TriggerField.validateBlur which just returns true
17728     validateBlur : function(){
17729         return !this.menu || !this.menu.isVisible();
17730     },
17731     
17732     getName: function()
17733     {
17734         // returns hidden if it's set..
17735         if (!this.rendered) {return ''};
17736         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17737         
17738     },
17739
17740     /**
17741      * Returns the current date value of the date field.
17742      * @return {Date} The date value
17743      */
17744     getValue : function(){
17745         
17746         return  this.hiddenField ?
17747                 this.hiddenField.value :
17748                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17749     },
17750
17751     /**
17752      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17753      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17754      * (the default format used is "m/d/y").
17755      * <br />Usage:
17756      * <pre><code>
17757 //All of these calls set the same date value (May 4, 2006)
17758
17759 //Pass a date object:
17760 var dt = new Date('5/4/06');
17761 dateField.setValue(dt);
17762
17763 //Pass a date string (default format):
17764 dateField.setValue('5/4/06');
17765
17766 //Pass a date string (custom format):
17767 dateField.format = 'Y-m-d';
17768 dateField.setValue('2006-5-4');
17769 </code></pre>
17770      * @param {String/Date} date The date or valid date string
17771      */
17772     setValue : function(date){
17773         if (this.hiddenField) {
17774             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17775         }
17776         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17777         // make sure the value field is always stored as a date..
17778         this.value = this.parseDate(date);
17779         
17780         
17781     },
17782
17783     // private
17784     parseDate : function(value){
17785                 
17786                 if (value instanceof Date) {
17787                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17788                                 return  '';
17789                         }
17790                         return value;
17791                 }
17792                 
17793                 
17794         if(!value || value instanceof Date){
17795             return value;
17796         }
17797         var v = Date.parseDate(value, this.format);
17798          if (!v && this.useIso) {
17799             v = Date.parseDate(value, 'Y-m-d');
17800         }
17801         if(!v && this.altFormats){
17802             if(!this.altFormatsArray){
17803                 this.altFormatsArray = this.altFormats.split("|");
17804             }
17805             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17806                 v = Date.parseDate(value, this.altFormatsArray[i]);
17807             }
17808         }
17809                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17810                         v = '';
17811                 }
17812         return v;
17813     },
17814
17815     // private
17816     formatDate : function(date, fmt){
17817         return (!date || !(date instanceof Date)) ?
17818                date : date.dateFormat(fmt || this.format);
17819     },
17820
17821     // private
17822     menuListeners : {
17823         select: function(m, d){
17824             
17825             this.setValue(d);
17826             this.fireEvent('select', this, d);
17827         },
17828         show : function(){ // retain focus styling
17829             this.onFocus();
17830         },
17831         hide : function(){
17832             this.focus.defer(10, this);
17833             var ml = this.menuListeners;
17834             this.menu.un("select", ml.select,  this);
17835             this.menu.un("show", ml.show,  this);
17836             this.menu.un("hide", ml.hide,  this);
17837         }
17838     },
17839
17840     // private
17841     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17842     onTriggerClick : function(){
17843         if(this.disabled){
17844             return;
17845         }
17846         if(this.menu == null){
17847             this.menu = new Roo.menu.DateMenu();
17848         }
17849         Roo.apply(this.menu.picker,  {
17850             showClear: this.allowBlank,
17851             minDate : this.minValue,
17852             maxDate : this.maxValue,
17853             disabledDatesRE : this.ddMatch,
17854             disabledDatesText : this.disabledDatesText,
17855             disabledDays : this.disabledDays,
17856             disabledDaysText : this.disabledDaysText,
17857             format : this.useIso ? 'Y-m-d' : this.format,
17858             minText : String.format(this.minText, this.formatDate(this.minValue)),
17859             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17860         });
17861         this.menu.on(Roo.apply({}, this.menuListeners, {
17862             scope:this
17863         }));
17864         this.menu.picker.setValue(this.getValue() || new Date());
17865         this.menu.show(this.el, "tl-bl?");
17866     },
17867
17868     beforeBlur : function(){
17869         var v = this.parseDate(this.getRawValue());
17870         if(v){
17871             this.setValue(v);
17872         }
17873     },
17874
17875     /*@
17876      * overide
17877      * 
17878      */
17879     isDirty : function() {
17880         if(this.disabled) {
17881             return false;
17882         }
17883         
17884         if(typeof(this.startValue) === 'undefined'){
17885             return false;
17886         }
17887         
17888         return String(this.getValue()) !== String(this.startValue);
17889         
17890     },
17891     // @overide
17892     cleanLeadingSpace : function(e)
17893     {
17894        return;
17895     }
17896     
17897 });/*
17898  * Based on:
17899  * Ext JS Library 1.1.1
17900  * Copyright(c) 2006-2007, Ext JS, LLC.
17901  *
17902  * Originally Released Under LGPL - original licence link has changed is not relivant.
17903  *
17904  * Fork - LGPL
17905  * <script type="text/javascript">
17906  */
17907  
17908 /**
17909  * @class Roo.form.MonthField
17910  * @extends Roo.form.TriggerField
17911  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17912 * @constructor
17913 * Create a new MonthField
17914 * @param {Object} config
17915  */
17916 Roo.form.MonthField = function(config){
17917     
17918     Roo.form.MonthField.superclass.constructor.call(this, config);
17919     
17920       this.addEvents({
17921          
17922         /**
17923          * @event select
17924          * Fires when a date is selected
17925              * @param {Roo.form.MonthFieeld} combo This combo box
17926              * @param {Date} date The date selected
17927              */
17928         'select' : true
17929          
17930     });
17931     
17932     
17933     if(typeof this.minValue == "string") {
17934         this.minValue = this.parseDate(this.minValue);
17935     }
17936     if(typeof this.maxValue == "string") {
17937         this.maxValue = this.parseDate(this.maxValue);
17938     }
17939     this.ddMatch = null;
17940     if(this.disabledDates){
17941         var dd = this.disabledDates;
17942         var re = "(?:";
17943         for(var i = 0; i < dd.length; i++){
17944             re += dd[i];
17945             if(i != dd.length-1) {
17946                 re += "|";
17947             }
17948         }
17949         this.ddMatch = new RegExp(re + ")");
17950     }
17951 };
17952
17953 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17954     /**
17955      * @cfg {String} format
17956      * The default date format string which can be overriden for localization support.  The format must be
17957      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17958      */
17959     format : "M Y",
17960     /**
17961      * @cfg {String} altFormats
17962      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17963      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17964      */
17965     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17966     /**
17967      * @cfg {Array} disabledDays
17968      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17969      */
17970     disabledDays : [0,1,2,3,4,5,6],
17971     /**
17972      * @cfg {String} disabledDaysText
17973      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17974      */
17975     disabledDaysText : "Disabled",
17976     /**
17977      * @cfg {Array} disabledDates
17978      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17979      * expression so they are very powerful. Some examples:
17980      * <ul>
17981      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17982      * <li>["03/08", "09/16"] would disable those days for every year</li>
17983      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17984      * <li>["03/../2006"] would disable every day in March 2006</li>
17985      * <li>["^03"] would disable every day in every March</li>
17986      * </ul>
17987      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17988      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17989      */
17990     disabledDates : null,
17991     /**
17992      * @cfg {String} disabledDatesText
17993      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17994      */
17995     disabledDatesText : "Disabled",
17996     /**
17997      * @cfg {Date/String} minValue
17998      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17999      * valid format (defaults to null).
18000      */
18001     minValue : null,
18002     /**
18003      * @cfg {Date/String} maxValue
18004      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18005      * valid format (defaults to null).
18006      */
18007     maxValue : null,
18008     /**
18009      * @cfg {String} minText
18010      * The error text to display when the date in the cell is before minValue (defaults to
18011      * 'The date in this field must be after {minValue}').
18012      */
18013     minText : "The date in this field must be equal to or after {0}",
18014     /**
18015      * @cfg {String} maxTextf
18016      * The error text to display when the date in the cell is after maxValue (defaults to
18017      * 'The date in this field must be before {maxValue}').
18018      */
18019     maxText : "The date in this field must be equal to or before {0}",
18020     /**
18021      * @cfg {String} invalidText
18022      * The error text to display when the date in the field is invalid (defaults to
18023      * '{value} is not a valid date - it must be in the format {format}').
18024      */
18025     invalidText : "{0} is not a valid date - it must be in the format {1}",
18026     /**
18027      * @cfg {String} triggerClass
18028      * An additional CSS class used to style the trigger button.  The trigger will always get the
18029      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18030      * which displays a calendar icon).
18031      */
18032     triggerClass : 'x-form-date-trigger',
18033     
18034
18035     /**
18036      * @cfg {Boolean} useIso
18037      * if enabled, then the date field will use a hidden field to store the 
18038      * real value as iso formated date. default (true)
18039      */ 
18040     useIso : true,
18041     /**
18042      * @cfg {String/Object} autoCreate
18043      * A DomHelper element spec, or true for a default element spec (defaults to
18044      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18045      */ 
18046     // private
18047     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18048     
18049     // private
18050     hiddenField: false,
18051     
18052     hideMonthPicker : false,
18053     
18054     onRender : function(ct, position)
18055     {
18056         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18057         if (this.useIso) {
18058             this.el.dom.removeAttribute('name'); 
18059             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18060                     'before', true);
18061             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18062             // prevent input submission
18063             this.hiddenName = this.name;
18064         }
18065             
18066             
18067     },
18068     
18069     // private
18070     validateValue : function(value)
18071     {
18072         value = this.formatDate(value);
18073         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18074             return false;
18075         }
18076         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18077              return true;
18078         }
18079         var svalue = value;
18080         value = this.parseDate(value);
18081         if(!value){
18082             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18083             return false;
18084         }
18085         var time = value.getTime();
18086         if(this.minValue && time < this.minValue.getTime()){
18087             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18088             return false;
18089         }
18090         if(this.maxValue && time > this.maxValue.getTime()){
18091             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18092             return false;
18093         }
18094         /*if(this.disabledDays){
18095             var day = value.getDay();
18096             for(var i = 0; i < this.disabledDays.length; i++) {
18097                 if(day === this.disabledDays[i]){
18098                     this.markInvalid(this.disabledDaysText);
18099                     return false;
18100                 }
18101             }
18102         }
18103         */
18104         var fvalue = this.formatDate(value);
18105         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18106             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18107             return false;
18108         }
18109         */
18110         return true;
18111     },
18112
18113     // private
18114     // Provides logic to override the default TriggerField.validateBlur which just returns true
18115     validateBlur : function(){
18116         return !this.menu || !this.menu.isVisible();
18117     },
18118
18119     /**
18120      * Returns the current date value of the date field.
18121      * @return {Date} The date value
18122      */
18123     getValue : function(){
18124         
18125         
18126         
18127         return  this.hiddenField ?
18128                 this.hiddenField.value :
18129                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18130     },
18131
18132     /**
18133      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18134      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18135      * (the default format used is "m/d/y").
18136      * <br />Usage:
18137      * <pre><code>
18138 //All of these calls set the same date value (May 4, 2006)
18139
18140 //Pass a date object:
18141 var dt = new Date('5/4/06');
18142 monthField.setValue(dt);
18143
18144 //Pass a date string (default format):
18145 monthField.setValue('5/4/06');
18146
18147 //Pass a date string (custom format):
18148 monthField.format = 'Y-m-d';
18149 monthField.setValue('2006-5-4');
18150 </code></pre>
18151      * @param {String/Date} date The date or valid date string
18152      */
18153     setValue : function(date){
18154         Roo.log('month setValue' + date);
18155         // can only be first of month..
18156         
18157         var val = this.parseDate(date);
18158         
18159         if (this.hiddenField) {
18160             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18161         }
18162         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18163         this.value = this.parseDate(date);
18164     },
18165
18166     // private
18167     parseDate : function(value){
18168         if(!value || value instanceof Date){
18169             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18170             return value;
18171         }
18172         var v = Date.parseDate(value, this.format);
18173         if (!v && this.useIso) {
18174             v = Date.parseDate(value, 'Y-m-d');
18175         }
18176         if (v) {
18177             // 
18178             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18179         }
18180         
18181         
18182         if(!v && this.altFormats){
18183             if(!this.altFormatsArray){
18184                 this.altFormatsArray = this.altFormats.split("|");
18185             }
18186             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18187                 v = Date.parseDate(value, this.altFormatsArray[i]);
18188             }
18189         }
18190         return v;
18191     },
18192
18193     // private
18194     formatDate : function(date, fmt){
18195         return (!date || !(date instanceof Date)) ?
18196                date : date.dateFormat(fmt || this.format);
18197     },
18198
18199     // private
18200     menuListeners : {
18201         select: function(m, d){
18202             this.setValue(d);
18203             this.fireEvent('select', this, d);
18204         },
18205         show : function(){ // retain focus styling
18206             this.onFocus();
18207         },
18208         hide : function(){
18209             this.focus.defer(10, this);
18210             var ml = this.menuListeners;
18211             this.menu.un("select", ml.select,  this);
18212             this.menu.un("show", ml.show,  this);
18213             this.menu.un("hide", ml.hide,  this);
18214         }
18215     },
18216     // private
18217     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18218     onTriggerClick : function(){
18219         if(this.disabled){
18220             return;
18221         }
18222         if(this.menu == null){
18223             this.menu = new Roo.menu.DateMenu();
18224            
18225         }
18226         
18227         Roo.apply(this.menu.picker,  {
18228             
18229             showClear: this.allowBlank,
18230             minDate : this.minValue,
18231             maxDate : this.maxValue,
18232             disabledDatesRE : this.ddMatch,
18233             disabledDatesText : this.disabledDatesText,
18234             
18235             format : this.useIso ? 'Y-m-d' : this.format,
18236             minText : String.format(this.minText, this.formatDate(this.minValue)),
18237             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18238             
18239         });
18240          this.menu.on(Roo.apply({}, this.menuListeners, {
18241             scope:this
18242         }));
18243        
18244         
18245         var m = this.menu;
18246         var p = m.picker;
18247         
18248         // hide month picker get's called when we called by 'before hide';
18249         
18250         var ignorehide = true;
18251         p.hideMonthPicker  = function(disableAnim){
18252             if (ignorehide) {
18253                 return;
18254             }
18255              if(this.monthPicker){
18256                 Roo.log("hideMonthPicker called");
18257                 if(disableAnim === true){
18258                     this.monthPicker.hide();
18259                 }else{
18260                     this.monthPicker.slideOut('t', {duration:.2});
18261                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18262                     p.fireEvent("select", this, this.value);
18263                     m.hide();
18264                 }
18265             }
18266         }
18267         
18268         Roo.log('picker set value');
18269         Roo.log(this.getValue());
18270         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18271         m.show(this.el, 'tl-bl?');
18272         ignorehide  = false;
18273         // this will trigger hideMonthPicker..
18274         
18275         
18276         // hidden the day picker
18277         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18278         
18279         
18280         
18281       
18282         
18283         p.showMonthPicker.defer(100, p);
18284     
18285         
18286        
18287     },
18288
18289     beforeBlur : function(){
18290         var v = this.parseDate(this.getRawValue());
18291         if(v){
18292             this.setValue(v);
18293         }
18294     }
18295
18296     /** @cfg {Boolean} grow @hide */
18297     /** @cfg {Number} growMin @hide */
18298     /** @cfg {Number} growMax @hide */
18299     /**
18300      * @hide
18301      * @method autoSize
18302      */
18303 });/*
18304  * Based on:
18305  * Ext JS Library 1.1.1
18306  * Copyright(c) 2006-2007, Ext JS, LLC.
18307  *
18308  * Originally Released Under LGPL - original licence link has changed is not relivant.
18309  *
18310  * Fork - LGPL
18311  * <script type="text/javascript">
18312  */
18313  
18314
18315 /**
18316  * @class Roo.form.ComboBox
18317  * @extends Roo.form.TriggerField
18318  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18319  * @constructor
18320  * Create a new ComboBox.
18321  * @param {Object} config Configuration options
18322  */
18323 Roo.form.ComboBox = function(config){
18324     Roo.form.ComboBox.superclass.constructor.call(this, config);
18325     this.addEvents({
18326         /**
18327          * @event expand
18328          * Fires when the dropdown list is expanded
18329              * @param {Roo.form.ComboBox} combo This combo box
18330              */
18331         'expand' : true,
18332         /**
18333          * @event collapse
18334          * Fires when the dropdown list is collapsed
18335              * @param {Roo.form.ComboBox} combo This combo box
18336              */
18337         'collapse' : true,
18338         /**
18339          * @event beforeselect
18340          * Fires before a list item is selected. Return false to cancel the selection.
18341              * @param {Roo.form.ComboBox} combo This combo box
18342              * @param {Roo.data.Record} record The data record returned from the underlying store
18343              * @param {Number} index The index of the selected item in the dropdown list
18344              */
18345         'beforeselect' : true,
18346         /**
18347          * @event select
18348          * Fires when a list item is selected
18349              * @param {Roo.form.ComboBox} combo This combo box
18350              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18351              * @param {Number} index The index of the selected item in the dropdown list
18352              */
18353         'select' : true,
18354         /**
18355          * @event beforequery
18356          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18357          * The event object passed has these properties:
18358              * @param {Roo.form.ComboBox} combo This combo box
18359              * @param {String} query The query
18360              * @param {Boolean} forceAll true to force "all" query
18361              * @param {Boolean} cancel true to cancel the query
18362              * @param {Object} e The query event object
18363              */
18364         'beforequery': true,
18365          /**
18366          * @event add
18367          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18368              * @param {Roo.form.ComboBox} combo This combo box
18369              */
18370         'add' : true,
18371         /**
18372          * @event edit
18373          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18374              * @param {Roo.form.ComboBox} combo This combo box
18375              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18376              */
18377         'edit' : true
18378         
18379         
18380     });
18381     if(this.transform){
18382         this.allowDomMove = false;
18383         var s = Roo.getDom(this.transform);
18384         if(!this.hiddenName){
18385             this.hiddenName = s.name;
18386         }
18387         if(!this.store){
18388             this.mode = 'local';
18389             var d = [], opts = s.options;
18390             for(var i = 0, len = opts.length;i < len; i++){
18391                 var o = opts[i];
18392                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18393                 if(o.selected) {
18394                     this.value = value;
18395                 }
18396                 d.push([value, o.text]);
18397             }
18398             this.store = new Roo.data.SimpleStore({
18399                 'id': 0,
18400                 fields: ['value', 'text'],
18401                 data : d
18402             });
18403             this.valueField = 'value';
18404             this.displayField = 'text';
18405         }
18406         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18407         if(!this.lazyRender){
18408             this.target = true;
18409             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18410             s.parentNode.removeChild(s); // remove it
18411             this.render(this.el.parentNode);
18412         }else{
18413             s.parentNode.removeChild(s); // remove it
18414         }
18415
18416     }
18417     if (this.store) {
18418         this.store = Roo.factory(this.store, Roo.data);
18419     }
18420     
18421     this.selectedIndex = -1;
18422     if(this.mode == 'local'){
18423         if(config.queryDelay === undefined){
18424             this.queryDelay = 10;
18425         }
18426         if(config.minChars === undefined){
18427             this.minChars = 0;
18428         }
18429     }
18430 };
18431
18432 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18433     /**
18434      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18435      */
18436     /**
18437      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18438      * rendering into an Roo.Editor, defaults to false)
18439      */
18440     /**
18441      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18442      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18443      */
18444     /**
18445      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18446      */
18447     /**
18448      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18449      * the dropdown list (defaults to undefined, with no header element)
18450      */
18451
18452      /**
18453      * @cfg {String/Roo.Template} tpl The template to use to render the output
18454      */
18455      
18456     // private
18457     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18458     /**
18459      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18460      */
18461     listWidth: undefined,
18462     /**
18463      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18464      * mode = 'remote' or 'text' if mode = 'local')
18465      */
18466     displayField: undefined,
18467     /**
18468      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18469      * mode = 'remote' or 'value' if mode = 'local'). 
18470      * Note: use of a valueField requires the user make a selection
18471      * in order for a value to be mapped.
18472      */
18473     valueField: undefined,
18474     
18475     
18476     /**
18477      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18478      * field's data value (defaults to the underlying DOM element's name)
18479      */
18480     hiddenName: undefined,
18481     /**
18482      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18483      */
18484     listClass: '',
18485     /**
18486      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18487      */
18488     selectedClass: 'x-combo-selected',
18489     /**
18490      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18491      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18492      * which displays a downward arrow icon).
18493      */
18494     triggerClass : 'x-form-arrow-trigger',
18495     /**
18496      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18497      */
18498     shadow:'sides',
18499     /**
18500      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18501      * anchor positions (defaults to 'tl-bl')
18502      */
18503     listAlign: 'tl-bl?',
18504     /**
18505      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18506      */
18507     maxHeight: 300,
18508     /**
18509      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18510      * query specified by the allQuery config option (defaults to 'query')
18511      */
18512     triggerAction: 'query',
18513     /**
18514      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18515      * (defaults to 4, does not apply if editable = false)
18516      */
18517     minChars : 4,
18518     /**
18519      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18520      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18521      */
18522     typeAhead: false,
18523     /**
18524      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18525      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18526      */
18527     queryDelay: 500,
18528     /**
18529      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18530      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18531      */
18532     pageSize: 0,
18533     /**
18534      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18535      * when editable = true (defaults to false)
18536      */
18537     selectOnFocus:false,
18538     /**
18539      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18540      */
18541     queryParam: 'query',
18542     /**
18543      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18544      * when mode = 'remote' (defaults to 'Loading...')
18545      */
18546     loadingText: 'Loading...',
18547     /**
18548      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18549      */
18550     resizable: false,
18551     /**
18552      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18553      */
18554     handleHeight : 8,
18555     /**
18556      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18557      * traditional select (defaults to true)
18558      */
18559     editable: true,
18560     /**
18561      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18562      */
18563     allQuery: '',
18564     /**
18565      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18566      */
18567     mode: 'remote',
18568     /**
18569      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18570      * listWidth has a higher value)
18571      */
18572     minListWidth : 70,
18573     /**
18574      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18575      * allow the user to set arbitrary text into the field (defaults to false)
18576      */
18577     forceSelection:false,
18578     /**
18579      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18580      * if typeAhead = true (defaults to 250)
18581      */
18582     typeAheadDelay : 250,
18583     /**
18584      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18585      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18586      */
18587     valueNotFoundText : undefined,
18588     /**
18589      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18590      */
18591     blockFocus : false,
18592     
18593     /**
18594      * @cfg {Boolean} disableClear Disable showing of clear button.
18595      */
18596     disableClear : false,
18597     /**
18598      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18599      */
18600     alwaysQuery : false,
18601     
18602     //private
18603     addicon : false,
18604     editicon: false,
18605     
18606     // element that contains real text value.. (when hidden is used..)
18607      
18608     // private
18609     onRender : function(ct, position)
18610     {
18611         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18612         
18613         if(this.hiddenName){
18614             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18615                     'before', true);
18616             this.hiddenField.value =
18617                 this.hiddenValue !== undefined ? this.hiddenValue :
18618                 this.value !== undefined ? this.value : '';
18619
18620             // prevent input submission
18621             this.el.dom.removeAttribute('name');
18622              
18623              
18624         }
18625         
18626         if(Roo.isGecko){
18627             this.el.dom.setAttribute('autocomplete', 'off');
18628         }
18629
18630         var cls = 'x-combo-list';
18631
18632         this.list = new Roo.Layer({
18633             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18634         });
18635
18636         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18637         this.list.setWidth(lw);
18638         this.list.swallowEvent('mousewheel');
18639         this.assetHeight = 0;
18640
18641         if(this.title){
18642             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18643             this.assetHeight += this.header.getHeight();
18644         }
18645
18646         this.innerList = this.list.createChild({cls:cls+'-inner'});
18647         this.innerList.on('mouseover', this.onViewOver, this);
18648         this.innerList.on('mousemove', this.onViewMove, this);
18649         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18650         
18651         if(this.allowBlank && !this.pageSize && !this.disableClear){
18652             this.footer = this.list.createChild({cls:cls+'-ft'});
18653             this.pageTb = new Roo.Toolbar(this.footer);
18654            
18655         }
18656         if(this.pageSize){
18657             this.footer = this.list.createChild({cls:cls+'-ft'});
18658             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18659                     {pageSize: this.pageSize});
18660             
18661         }
18662         
18663         if (this.pageTb && this.allowBlank && !this.disableClear) {
18664             var _this = this;
18665             this.pageTb.add(new Roo.Toolbar.Fill(), {
18666                 cls: 'x-btn-icon x-btn-clear',
18667                 text: '&#160;',
18668                 handler: function()
18669                 {
18670                     _this.collapse();
18671                     _this.clearValue();
18672                     _this.onSelect(false, -1);
18673                 }
18674             });
18675         }
18676         if (this.footer) {
18677             this.assetHeight += this.footer.getHeight();
18678         }
18679         
18680
18681         if(!this.tpl){
18682             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18683         }
18684
18685         this.view = new Roo.View(this.innerList, this.tpl, {
18686             singleSelect:true,
18687             store: this.store,
18688             selectedClass: this.selectedClass
18689         });
18690
18691         this.view.on('click', this.onViewClick, this);
18692
18693         this.store.on('beforeload', this.onBeforeLoad, this);
18694         this.store.on('load', this.onLoad, this);
18695         this.store.on('loadexception', this.onLoadException, this);
18696
18697         if(this.resizable){
18698             this.resizer = new Roo.Resizable(this.list,  {
18699                pinned:true, handles:'se'
18700             });
18701             this.resizer.on('resize', function(r, w, h){
18702                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18703                 this.listWidth = w;
18704                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18705                 this.restrictHeight();
18706             }, this);
18707             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18708         }
18709         if(!this.editable){
18710             this.editable = true;
18711             this.setEditable(false);
18712         }  
18713         
18714         
18715         if (typeof(this.events.add.listeners) != 'undefined') {
18716             
18717             this.addicon = this.wrap.createChild(
18718                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18719        
18720             this.addicon.on('click', function(e) {
18721                 this.fireEvent('add', this);
18722             }, this);
18723         }
18724         if (typeof(this.events.edit.listeners) != 'undefined') {
18725             
18726             this.editicon = this.wrap.createChild(
18727                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18728             if (this.addicon) {
18729                 this.editicon.setStyle('margin-left', '40px');
18730             }
18731             this.editicon.on('click', function(e) {
18732                 
18733                 // we fire even  if inothing is selected..
18734                 this.fireEvent('edit', this, this.lastData );
18735                 
18736             }, this);
18737         }
18738         
18739         
18740         
18741     },
18742
18743     // private
18744     initEvents : function(){
18745         Roo.form.ComboBox.superclass.initEvents.call(this);
18746
18747         this.keyNav = new Roo.KeyNav(this.el, {
18748             "up" : function(e){
18749                 this.inKeyMode = true;
18750                 this.selectPrev();
18751             },
18752
18753             "down" : function(e){
18754                 if(!this.isExpanded()){
18755                     this.onTriggerClick();
18756                 }else{
18757                     this.inKeyMode = true;
18758                     this.selectNext();
18759                 }
18760             },
18761
18762             "enter" : function(e){
18763                 this.onViewClick();
18764                 //return true;
18765             },
18766
18767             "esc" : function(e){
18768                 this.collapse();
18769             },
18770
18771             "tab" : function(e){
18772                 this.onViewClick(false);
18773                 this.fireEvent("specialkey", this, e);
18774                 return true;
18775             },
18776
18777             scope : this,
18778
18779             doRelay : function(foo, bar, hname){
18780                 if(hname == 'down' || this.scope.isExpanded()){
18781                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18782                 }
18783                 return true;
18784             },
18785
18786             forceKeyDown: true
18787         });
18788         this.queryDelay = Math.max(this.queryDelay || 10,
18789                 this.mode == 'local' ? 10 : 250);
18790         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18791         if(this.typeAhead){
18792             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18793         }
18794         if(this.editable !== false){
18795             this.el.on("keyup", this.onKeyUp, this);
18796         }
18797         if(this.forceSelection){
18798             this.on('blur', this.doForce, this);
18799         }
18800     },
18801
18802     onDestroy : function(){
18803         if(this.view){
18804             this.view.setStore(null);
18805             this.view.el.removeAllListeners();
18806             this.view.el.remove();
18807             this.view.purgeListeners();
18808         }
18809         if(this.list){
18810             this.list.destroy();
18811         }
18812         if(this.store){
18813             this.store.un('beforeload', this.onBeforeLoad, this);
18814             this.store.un('load', this.onLoad, this);
18815             this.store.un('loadexception', this.onLoadException, this);
18816         }
18817         Roo.form.ComboBox.superclass.onDestroy.call(this);
18818     },
18819
18820     // private
18821     fireKey : function(e){
18822         if(e.isNavKeyPress() && !this.list.isVisible()){
18823             this.fireEvent("specialkey", this, e);
18824         }
18825     },
18826
18827     // private
18828     onResize: function(w, h){
18829         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18830         
18831         if(typeof w != 'number'){
18832             // we do not handle it!?!?
18833             return;
18834         }
18835         var tw = this.trigger.getWidth();
18836         tw += this.addicon ? this.addicon.getWidth() : 0;
18837         tw += this.editicon ? this.editicon.getWidth() : 0;
18838         var x = w - tw;
18839         this.el.setWidth( this.adjustWidth('input', x));
18840             
18841         this.trigger.setStyle('left', x+'px');
18842         
18843         if(this.list && this.listWidth === undefined){
18844             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18845             this.list.setWidth(lw);
18846             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18847         }
18848         
18849     
18850         
18851     },
18852
18853     /**
18854      * Allow or prevent the user from directly editing the field text.  If false is passed,
18855      * the user will only be able to select from the items defined in the dropdown list.  This method
18856      * is the runtime equivalent of setting the 'editable' config option at config time.
18857      * @param {Boolean} value True to allow the user to directly edit the field text
18858      */
18859     setEditable : function(value){
18860         if(value == this.editable){
18861             return;
18862         }
18863         this.editable = value;
18864         if(!value){
18865             this.el.dom.setAttribute('readOnly', true);
18866             this.el.on('mousedown', this.onTriggerClick,  this);
18867             this.el.addClass('x-combo-noedit');
18868         }else{
18869             this.el.dom.setAttribute('readOnly', false);
18870             this.el.un('mousedown', this.onTriggerClick,  this);
18871             this.el.removeClass('x-combo-noedit');
18872         }
18873     },
18874
18875     // private
18876     onBeforeLoad : function(){
18877         if(!this.hasFocus){
18878             return;
18879         }
18880         this.innerList.update(this.loadingText ?
18881                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18882         this.restrictHeight();
18883         this.selectedIndex = -1;
18884     },
18885
18886     // private
18887     onLoad : function(){
18888         if(!this.hasFocus){
18889             return;
18890         }
18891         if(this.store.getCount() > 0){
18892             this.expand();
18893             this.restrictHeight();
18894             if(this.lastQuery == this.allQuery){
18895                 if(this.editable){
18896                     this.el.dom.select();
18897                 }
18898                 if(!this.selectByValue(this.value, true)){
18899                     this.select(0, true);
18900                 }
18901             }else{
18902                 this.selectNext();
18903                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18904                     this.taTask.delay(this.typeAheadDelay);
18905                 }
18906             }
18907         }else{
18908             this.onEmptyResults();
18909         }
18910         //this.el.focus();
18911     },
18912     // private
18913     onLoadException : function()
18914     {
18915         this.collapse();
18916         Roo.log(this.store.reader.jsonData);
18917         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18918             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18919         }
18920         
18921         
18922     },
18923     // private
18924     onTypeAhead : function(){
18925         if(this.store.getCount() > 0){
18926             var r = this.store.getAt(0);
18927             var newValue = r.data[this.displayField];
18928             var len = newValue.length;
18929             var selStart = this.getRawValue().length;
18930             if(selStart != len){
18931                 this.setRawValue(newValue);
18932                 this.selectText(selStart, newValue.length);
18933             }
18934         }
18935     },
18936
18937     // private
18938     onSelect : function(record, index){
18939         if(this.fireEvent('beforeselect', this, record, index) !== false){
18940             this.setFromData(index > -1 ? record.data : false);
18941             this.collapse();
18942             this.fireEvent('select', this, record, index);
18943         }
18944     },
18945
18946     /**
18947      * Returns the currently selected field value or empty string if no value is set.
18948      * @return {String} value The selected value
18949      */
18950     getValue : function(){
18951         if(this.valueField){
18952             return typeof this.value != 'undefined' ? this.value : '';
18953         }
18954         return Roo.form.ComboBox.superclass.getValue.call(this);
18955     },
18956
18957     /**
18958      * Clears any text/value currently set in the field
18959      */
18960     clearValue : function(){
18961         if(this.hiddenField){
18962             this.hiddenField.value = '';
18963         }
18964         this.value = '';
18965         this.setRawValue('');
18966         this.lastSelectionText = '';
18967         
18968     },
18969
18970     /**
18971      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18972      * will be displayed in the field.  If the value does not match the data value of an existing item,
18973      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18974      * Otherwise the field will be blank (although the value will still be set).
18975      * @param {String} value The value to match
18976      */
18977     setValue : function(v){
18978         var text = v;
18979         if(this.valueField){
18980             var r = this.findRecord(this.valueField, v);
18981             if(r){
18982                 text = r.data[this.displayField];
18983             }else if(this.valueNotFoundText !== undefined){
18984                 text = this.valueNotFoundText;
18985             }
18986         }
18987         this.lastSelectionText = text;
18988         if(this.hiddenField){
18989             this.hiddenField.value = v;
18990         }
18991         Roo.form.ComboBox.superclass.setValue.call(this, text);
18992         this.value = v;
18993     },
18994     /**
18995      * @property {Object} the last set data for the element
18996      */
18997     
18998     lastData : false,
18999     /**
19000      * Sets the value of the field based on a object which is related to the record format for the store.
19001      * @param {Object} value the value to set as. or false on reset?
19002      */
19003     setFromData : function(o){
19004         var dv = ''; // display value
19005         var vv = ''; // value value..
19006         this.lastData = o;
19007         if (this.displayField) {
19008             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19009         } else {
19010             // this is an error condition!!!
19011             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19012         }
19013         
19014         if(this.valueField){
19015             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19016         }
19017         if(this.hiddenField){
19018             this.hiddenField.value = vv;
19019             
19020             this.lastSelectionText = dv;
19021             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19022             this.value = vv;
19023             return;
19024         }
19025         // no hidden field.. - we store the value in 'value', but still display
19026         // display field!!!!
19027         this.lastSelectionText = dv;
19028         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19029         this.value = vv;
19030         
19031         
19032     },
19033     // private
19034     reset : function(){
19035         // overridden so that last data is reset..
19036         this.setValue(this.resetValue);
19037         this.originalValue = this.getValue();
19038         this.clearInvalid();
19039         this.lastData = false;
19040         if (this.view) {
19041             this.view.clearSelections();
19042         }
19043     },
19044     // private
19045     findRecord : function(prop, value){
19046         var record;
19047         if(this.store.getCount() > 0){
19048             this.store.each(function(r){
19049                 if(r.data[prop] == value){
19050                     record = r;
19051                     return false;
19052                 }
19053                 return true;
19054             });
19055         }
19056         return record;
19057     },
19058     
19059     getName: function()
19060     {
19061         // returns hidden if it's set..
19062         if (!this.rendered) {return ''};
19063         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19064         
19065     },
19066     // private
19067     onViewMove : function(e, t){
19068         this.inKeyMode = false;
19069     },
19070
19071     // private
19072     onViewOver : function(e, t){
19073         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19074             return;
19075         }
19076         var item = this.view.findItemFromChild(t);
19077         if(item){
19078             var index = this.view.indexOf(item);
19079             this.select(index, false);
19080         }
19081     },
19082
19083     // private
19084     onViewClick : function(doFocus)
19085     {
19086         var index = this.view.getSelectedIndexes()[0];
19087         var r = this.store.getAt(index);
19088         if(r){
19089             this.onSelect(r, index);
19090         }
19091         if(doFocus !== false && !this.blockFocus){
19092             this.el.focus();
19093         }
19094     },
19095
19096     // private
19097     restrictHeight : function(){
19098         this.innerList.dom.style.height = '';
19099         var inner = this.innerList.dom;
19100         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19101         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19102         this.list.beginUpdate();
19103         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19104         this.list.alignTo(this.el, this.listAlign);
19105         this.list.endUpdate();
19106     },
19107
19108     // private
19109     onEmptyResults : function(){
19110         this.collapse();
19111     },
19112
19113     /**
19114      * Returns true if the dropdown list is expanded, else false.
19115      */
19116     isExpanded : function(){
19117         return this.list.isVisible();
19118     },
19119
19120     /**
19121      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19122      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19123      * @param {String} value The data value of the item to select
19124      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19125      * selected item if it is not currently in view (defaults to true)
19126      * @return {Boolean} True if the value matched an item in the list, else false
19127      */
19128     selectByValue : function(v, scrollIntoView){
19129         if(v !== undefined && v !== null){
19130             var r = this.findRecord(this.valueField || this.displayField, v);
19131             if(r){
19132                 this.select(this.store.indexOf(r), scrollIntoView);
19133                 return true;
19134             }
19135         }
19136         return false;
19137     },
19138
19139     /**
19140      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19141      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19142      * @param {Number} index The zero-based index of the list item to select
19143      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19144      * selected item if it is not currently in view (defaults to true)
19145      */
19146     select : function(index, scrollIntoView){
19147         this.selectedIndex = index;
19148         this.view.select(index);
19149         if(scrollIntoView !== false){
19150             var el = this.view.getNode(index);
19151             if(el){
19152                 this.innerList.scrollChildIntoView(el, false);
19153             }
19154         }
19155     },
19156
19157     // private
19158     selectNext : function(){
19159         var ct = this.store.getCount();
19160         if(ct > 0){
19161             if(this.selectedIndex == -1){
19162                 this.select(0);
19163             }else if(this.selectedIndex < ct-1){
19164                 this.select(this.selectedIndex+1);
19165             }
19166         }
19167     },
19168
19169     // private
19170     selectPrev : function(){
19171         var ct = this.store.getCount();
19172         if(ct > 0){
19173             if(this.selectedIndex == -1){
19174                 this.select(0);
19175             }else if(this.selectedIndex != 0){
19176                 this.select(this.selectedIndex-1);
19177             }
19178         }
19179     },
19180
19181     // private
19182     onKeyUp : function(e){
19183         if(this.editable !== false && !e.isSpecialKey()){
19184             this.lastKey = e.getKey();
19185             this.dqTask.delay(this.queryDelay);
19186         }
19187     },
19188
19189     // private
19190     validateBlur : function(){
19191         return !this.list || !this.list.isVisible();   
19192     },
19193
19194     // private
19195     initQuery : function(){
19196         this.doQuery(this.getRawValue());
19197     },
19198
19199     // private
19200     doForce : function(){
19201         if(this.el.dom.value.length > 0){
19202             this.el.dom.value =
19203                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19204              
19205         }
19206     },
19207
19208     /**
19209      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19210      * query allowing the query action to be canceled if needed.
19211      * @param {String} query The SQL query to execute
19212      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19213      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19214      * saved in the current store (defaults to false)
19215      */
19216     doQuery : function(q, forceAll){
19217         if(q === undefined || q === null){
19218             q = '';
19219         }
19220         var qe = {
19221             query: q,
19222             forceAll: forceAll,
19223             combo: this,
19224             cancel:false
19225         };
19226         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19227             return false;
19228         }
19229         q = qe.query;
19230         forceAll = qe.forceAll;
19231         if(forceAll === true || (q.length >= this.minChars)){
19232             if(this.lastQuery != q || this.alwaysQuery){
19233                 this.lastQuery = q;
19234                 if(this.mode == 'local'){
19235                     this.selectedIndex = -1;
19236                     if(forceAll){
19237                         this.store.clearFilter();
19238                     }else{
19239                         this.store.filter(this.displayField, q);
19240                     }
19241                     this.onLoad();
19242                 }else{
19243                     this.store.baseParams[this.queryParam] = q;
19244                     this.store.load({
19245                         params: this.getParams(q)
19246                     });
19247                     this.expand();
19248                 }
19249             }else{
19250                 this.selectedIndex = -1;
19251                 this.onLoad();   
19252             }
19253         }
19254     },
19255
19256     // private
19257     getParams : function(q){
19258         var p = {};
19259         //p[this.queryParam] = q;
19260         if(this.pageSize){
19261             p.start = 0;
19262             p.limit = this.pageSize;
19263         }
19264         return p;
19265     },
19266
19267     /**
19268      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19269      */
19270     collapse : function(){
19271         if(!this.isExpanded()){
19272             return;
19273         }
19274         this.list.hide();
19275         Roo.get(document).un('mousedown', this.collapseIf, this);
19276         Roo.get(document).un('mousewheel', this.collapseIf, this);
19277         if (!this.editable) {
19278             Roo.get(document).un('keydown', this.listKeyPress, this);
19279         }
19280         this.fireEvent('collapse', this);
19281     },
19282
19283     // private
19284     collapseIf : function(e){
19285         if(!e.within(this.wrap) && !e.within(this.list)){
19286             this.collapse();
19287         }
19288     },
19289
19290     /**
19291      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19292      */
19293     expand : function(){
19294         if(this.isExpanded() || !this.hasFocus){
19295             return;
19296         }
19297         this.list.alignTo(this.el, this.listAlign);
19298         this.list.show();
19299         Roo.get(document).on('mousedown', this.collapseIf, this);
19300         Roo.get(document).on('mousewheel', this.collapseIf, this);
19301         if (!this.editable) {
19302             Roo.get(document).on('keydown', this.listKeyPress, this);
19303         }
19304         
19305         this.fireEvent('expand', this);
19306     },
19307
19308     // private
19309     // Implements the default empty TriggerField.onTriggerClick function
19310     onTriggerClick : function(){
19311         if(this.disabled){
19312             return;
19313         }
19314         if(this.isExpanded()){
19315             this.collapse();
19316             if (!this.blockFocus) {
19317                 this.el.focus();
19318             }
19319             
19320         }else {
19321             this.hasFocus = true;
19322             if(this.triggerAction == 'all') {
19323                 this.doQuery(this.allQuery, true);
19324             } else {
19325                 this.doQuery(this.getRawValue());
19326             }
19327             if (!this.blockFocus) {
19328                 this.el.focus();
19329             }
19330         }
19331     },
19332     listKeyPress : function(e)
19333     {
19334         //Roo.log('listkeypress');
19335         // scroll to first matching element based on key pres..
19336         if (e.isSpecialKey()) {
19337             return false;
19338         }
19339         var k = String.fromCharCode(e.getKey()).toUpperCase();
19340         //Roo.log(k);
19341         var match  = false;
19342         var csel = this.view.getSelectedNodes();
19343         var cselitem = false;
19344         if (csel.length) {
19345             var ix = this.view.indexOf(csel[0]);
19346             cselitem  = this.store.getAt(ix);
19347             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19348                 cselitem = false;
19349             }
19350             
19351         }
19352         
19353         this.store.each(function(v) { 
19354             if (cselitem) {
19355                 // start at existing selection.
19356                 if (cselitem.id == v.id) {
19357                     cselitem = false;
19358                 }
19359                 return;
19360             }
19361                 
19362             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19363                 match = this.store.indexOf(v);
19364                 return false;
19365             }
19366         }, this);
19367         
19368         if (match === false) {
19369             return true; // no more action?
19370         }
19371         // scroll to?
19372         this.view.select(match);
19373         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19374         sn.scrollIntoView(sn.dom.parentNode, false);
19375     } 
19376
19377     /** 
19378     * @cfg {Boolean} grow 
19379     * @hide 
19380     */
19381     /** 
19382     * @cfg {Number} growMin 
19383     * @hide 
19384     */
19385     /** 
19386     * @cfg {Number} growMax 
19387     * @hide 
19388     */
19389     /**
19390      * @hide
19391      * @method autoSize
19392      */
19393 });/*
19394  * Copyright(c) 2010-2012, Roo J Solutions Limited
19395  *
19396  * Licence LGPL
19397  *
19398  */
19399
19400 /**
19401  * @class Roo.form.ComboBoxArray
19402  * @extends Roo.form.TextField
19403  * A facebook style adder... for lists of email / people / countries  etc...
19404  * pick multiple items from a combo box, and shows each one.
19405  *
19406  *  Fred [x]  Brian [x]  [Pick another |v]
19407  *
19408  *
19409  *  For this to work: it needs various extra information
19410  *    - normal combo problay has
19411  *      name, hiddenName
19412  *    + displayField, valueField
19413  *
19414  *    For our purpose...
19415  *
19416  *
19417  *   If we change from 'extends' to wrapping...
19418  *   
19419  *  
19420  *
19421  
19422  
19423  * @constructor
19424  * Create a new ComboBoxArray.
19425  * @param {Object} config Configuration options
19426  */
19427  
19428
19429 Roo.form.ComboBoxArray = function(config)
19430 {
19431     this.addEvents({
19432         /**
19433          * @event beforeremove
19434          * Fires before remove the value from the list
19435              * @param {Roo.form.ComboBoxArray} _self This combo box array
19436              * @param {Roo.form.ComboBoxArray.Item} item removed item
19437              */
19438         'beforeremove' : true,
19439         /**
19440          * @event remove
19441          * Fires when remove the value from the list
19442              * @param {Roo.form.ComboBoxArray} _self This combo box array
19443              * @param {Roo.form.ComboBoxArray.Item} item removed item
19444              */
19445         'remove' : true
19446         
19447         
19448     });
19449     
19450     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19451     
19452     this.items = new Roo.util.MixedCollection(false);
19453     
19454     // construct the child combo...
19455     
19456     
19457     
19458     
19459    
19460     
19461 }
19462
19463  
19464 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19465
19466     /**
19467      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19468      */
19469     
19470     lastData : false,
19471     
19472     // behavies liek a hiddne field
19473     inputType:      'hidden',
19474     /**
19475      * @cfg {Number} width The width of the box that displays the selected element
19476      */ 
19477     width:          300,
19478
19479     
19480     
19481     /**
19482      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19483      */
19484     name : false,
19485     /**
19486      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19487      */
19488     hiddenName : false,
19489       /**
19490      * @cfg {String} seperator    The value seperator normally ',' 
19491      */
19492     seperator : ',',
19493     
19494     // private the array of items that are displayed..
19495     items  : false,
19496     // private - the hidden field el.
19497     hiddenEl : false,
19498     // private - the filed el..
19499     el : false,
19500     
19501     //validateValue : function() { return true; }, // all values are ok!
19502     //onAddClick: function() { },
19503     
19504     onRender : function(ct, position) 
19505     {
19506         
19507         // create the standard hidden element
19508         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19509         
19510         
19511         // give fake names to child combo;
19512         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19513         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19514         
19515         this.combo = Roo.factory(this.combo, Roo.form);
19516         this.combo.onRender(ct, position);
19517         if (typeof(this.combo.width) != 'undefined') {
19518             this.combo.onResize(this.combo.width,0);
19519         }
19520         
19521         this.combo.initEvents();
19522         
19523         // assigned so form know we need to do this..
19524         this.store          = this.combo.store;
19525         this.valueField     = this.combo.valueField;
19526         this.displayField   = this.combo.displayField ;
19527         
19528         
19529         this.combo.wrap.addClass('x-cbarray-grp');
19530         
19531         var cbwrap = this.combo.wrap.createChild(
19532             {tag: 'div', cls: 'x-cbarray-cb'},
19533             this.combo.el.dom
19534         );
19535         
19536              
19537         this.hiddenEl = this.combo.wrap.createChild({
19538             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19539         });
19540         this.el = this.combo.wrap.createChild({
19541             tag: 'input',  type:'hidden' , name: this.name, value : ''
19542         });
19543          //   this.el.dom.removeAttribute("name");
19544         
19545         
19546         this.outerWrap = this.combo.wrap;
19547         this.wrap = cbwrap;
19548         
19549         this.outerWrap.setWidth(this.width);
19550         this.outerWrap.dom.removeChild(this.el.dom);
19551         
19552         this.wrap.dom.appendChild(this.el.dom);
19553         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19554         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19555         
19556         this.combo.trigger.setStyle('position','relative');
19557         this.combo.trigger.setStyle('left', '0px');
19558         this.combo.trigger.setStyle('top', '2px');
19559         
19560         this.combo.el.setStyle('vertical-align', 'text-bottom');
19561         
19562         //this.trigger.setStyle('vertical-align', 'top');
19563         
19564         // this should use the code from combo really... on('add' ....)
19565         if (this.adder) {
19566             
19567         
19568             this.adder = this.outerWrap.createChild(
19569                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19570             var _t = this;
19571             this.adder.on('click', function(e) {
19572                 _t.fireEvent('adderclick', this, e);
19573             }, _t);
19574         }
19575         //var _t = this;
19576         //this.adder.on('click', this.onAddClick, _t);
19577         
19578         
19579         this.combo.on('select', function(cb, rec, ix) {
19580             this.addItem(rec.data);
19581             
19582             cb.setValue('');
19583             cb.el.dom.value = '';
19584             //cb.lastData = rec.data;
19585             // add to list
19586             
19587         }, this);
19588         
19589         
19590     },
19591     
19592     
19593     getName: function()
19594     {
19595         // returns hidden if it's set..
19596         if (!this.rendered) {return ''};
19597         return  this.hiddenName ? this.hiddenName : this.name;
19598         
19599     },
19600     
19601     
19602     onResize: function(w, h){
19603         
19604         return;
19605         // not sure if this is needed..
19606         //this.combo.onResize(w,h);
19607         
19608         if(typeof w != 'number'){
19609             // we do not handle it!?!?
19610             return;
19611         }
19612         var tw = this.combo.trigger.getWidth();
19613         tw += this.addicon ? this.addicon.getWidth() : 0;
19614         tw += this.editicon ? this.editicon.getWidth() : 0;
19615         var x = w - tw;
19616         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19617             
19618         this.combo.trigger.setStyle('left', '0px');
19619         
19620         if(this.list && this.listWidth === undefined){
19621             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19622             this.list.setWidth(lw);
19623             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19624         }
19625         
19626     
19627         
19628     },
19629     
19630     addItem: function(rec)
19631     {
19632         var valueField = this.combo.valueField;
19633         var displayField = this.combo.displayField;
19634         
19635         if (this.items.indexOfKey(rec[valueField]) > -1) {
19636             //console.log("GOT " + rec.data.id);
19637             return;
19638         }
19639         
19640         var x = new Roo.form.ComboBoxArray.Item({
19641             //id : rec[this.idField],
19642             data : rec,
19643             displayField : displayField ,
19644             tipField : displayField ,
19645             cb : this
19646         });
19647         // use the 
19648         this.items.add(rec[valueField],x);
19649         // add it before the element..
19650         this.updateHiddenEl();
19651         x.render(this.outerWrap, this.wrap.dom);
19652         // add the image handler..
19653     },
19654     
19655     updateHiddenEl : function()
19656     {
19657         this.validate();
19658         if (!this.hiddenEl) {
19659             return;
19660         }
19661         var ar = [];
19662         var idField = this.combo.valueField;
19663         
19664         this.items.each(function(f) {
19665             ar.push(f.data[idField]);
19666         });
19667         this.hiddenEl.dom.value = ar.join(this.seperator);
19668         this.validate();
19669     },
19670     
19671     reset : function()
19672     {
19673         this.items.clear();
19674         
19675         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19676            el.remove();
19677         });
19678         
19679         this.el.dom.value = '';
19680         if (this.hiddenEl) {
19681             this.hiddenEl.dom.value = '';
19682         }
19683         
19684     },
19685     getValue: function()
19686     {
19687         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19688     },
19689     setValue: function(v) // not a valid action - must use addItems..
19690     {
19691         
19692         this.reset();
19693          
19694         if (this.store.isLocal && (typeof(v) == 'string')) {
19695             // then we can use the store to find the values..
19696             // comma seperated at present.. this needs to allow JSON based encoding..
19697             this.hiddenEl.value  = v;
19698             var v_ar = [];
19699             Roo.each(v.split(this.seperator), function(k) {
19700                 Roo.log("CHECK " + this.valueField + ',' + k);
19701                 var li = this.store.query(this.valueField, k);
19702                 if (!li.length) {
19703                     return;
19704                 }
19705                 var add = {};
19706                 add[this.valueField] = k;
19707                 add[this.displayField] = li.item(0).data[this.displayField];
19708                 
19709                 this.addItem(add);
19710             }, this) 
19711              
19712         }
19713         if (typeof(v) == 'object' ) {
19714             // then let's assume it's an array of objects..
19715             Roo.each(v, function(l) {
19716                 var add = l;
19717                 if (typeof(l) == 'string') {
19718                     add = {};
19719                     add[this.valueField] = l;
19720                     add[this.displayField] = l
19721                 }
19722                 this.addItem(add);
19723             }, this);
19724              
19725         }
19726         
19727         
19728     },
19729     setFromData: function(v)
19730     {
19731         // this recieves an object, if setValues is called.
19732         this.reset();
19733         this.el.dom.value = v[this.displayField];
19734         this.hiddenEl.dom.value = v[this.valueField];
19735         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19736             return;
19737         }
19738         var kv = v[this.valueField];
19739         var dv = v[this.displayField];
19740         kv = typeof(kv) != 'string' ? '' : kv;
19741         dv = typeof(dv) != 'string' ? '' : dv;
19742         
19743         
19744         var keys = kv.split(this.seperator);
19745         var display = dv.split(this.seperator);
19746         for (var i = 0 ; i < keys.length; i++) {
19747             add = {};
19748             add[this.valueField] = keys[i];
19749             add[this.displayField] = display[i];
19750             this.addItem(add);
19751         }
19752       
19753         
19754     },
19755     
19756     /**
19757      * Validates the combox array value
19758      * @return {Boolean} True if the value is valid, else false
19759      */
19760     validate : function(){
19761         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19762             this.clearInvalid();
19763             return true;
19764         }
19765         return false;
19766     },
19767     
19768     validateValue : function(value){
19769         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19770         
19771     },
19772     
19773     /*@
19774      * overide
19775      * 
19776      */
19777     isDirty : function() {
19778         if(this.disabled) {
19779             return false;
19780         }
19781         
19782         try {
19783             var d = Roo.decode(String(this.originalValue));
19784         } catch (e) {
19785             return String(this.getValue()) !== String(this.originalValue);
19786         }
19787         
19788         var originalValue = [];
19789         
19790         for (var i = 0; i < d.length; i++){
19791             originalValue.push(d[i][this.valueField]);
19792         }
19793         
19794         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19795         
19796     }
19797     
19798 });
19799
19800
19801
19802 /**
19803  * @class Roo.form.ComboBoxArray.Item
19804  * @extends Roo.BoxComponent
19805  * A selected item in the list
19806  *  Fred [x]  Brian [x]  [Pick another |v]
19807  * 
19808  * @constructor
19809  * Create a new item.
19810  * @param {Object} config Configuration options
19811  */
19812  
19813 Roo.form.ComboBoxArray.Item = function(config) {
19814     config.id = Roo.id();
19815     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19816 }
19817
19818 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19819     data : {},
19820     cb: false,
19821     displayField : false,
19822     tipField : false,
19823     
19824     
19825     defaultAutoCreate : {
19826         tag: 'div',
19827         cls: 'x-cbarray-item',
19828         cn : [ 
19829             { tag: 'div' },
19830             {
19831                 tag: 'img',
19832                 width:16,
19833                 height : 16,
19834                 src : Roo.BLANK_IMAGE_URL ,
19835                 align: 'center'
19836             }
19837         ]
19838         
19839     },
19840     
19841  
19842     onRender : function(ct, position)
19843     {
19844         Roo.form.Field.superclass.onRender.call(this, ct, position);
19845         
19846         if(!this.el){
19847             var cfg = this.getAutoCreate();
19848             this.el = ct.createChild(cfg, position);
19849         }
19850         
19851         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19852         
19853         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19854             this.cb.renderer(this.data) :
19855             String.format('{0}',this.data[this.displayField]);
19856         
19857             
19858         this.el.child('div').dom.setAttribute('qtip',
19859                         String.format('{0}',this.data[this.tipField])
19860         );
19861         
19862         this.el.child('img').on('click', this.remove, this);
19863         
19864     },
19865    
19866     remove : function()
19867     {
19868         if(this.cb.disabled){
19869             return;
19870         }
19871         
19872         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19873             this.cb.items.remove(this);
19874             this.el.child('img').un('click', this.remove, this);
19875             this.el.remove();
19876             this.cb.updateHiddenEl();
19877
19878             this.cb.fireEvent('remove', this.cb, this);
19879         }
19880         
19881     }
19882 });/*
19883  * RooJS Library 1.1.1
19884  * Copyright(c) 2008-2011  Alan Knowles
19885  *
19886  * License - LGPL
19887  */
19888  
19889
19890 /**
19891  * @class Roo.form.ComboNested
19892  * @extends Roo.form.ComboBox
19893  * A combobox for that allows selection of nested items in a list,
19894  * eg.
19895  *
19896  *  Book
19897  *    -> red
19898  *    -> green
19899  *  Table
19900  *    -> square
19901  *      ->red
19902  *      ->green
19903  *    -> rectangle
19904  *      ->green
19905  *      
19906  * 
19907  * @constructor
19908  * Create a new ComboNested
19909  * @param {Object} config Configuration options
19910  */
19911 Roo.form.ComboNested = function(config){
19912     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19913     // should verify some data...
19914     // like
19915     // hiddenName = required..
19916     // displayField = required
19917     // valudField == required
19918     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19919     var _t = this;
19920     Roo.each(req, function(e) {
19921         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19922             throw "Roo.form.ComboNested : missing value for: " + e;
19923         }
19924     });
19925      
19926     
19927 };
19928
19929 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19930    
19931     /*
19932      * @config {Number} max Number of columns to show
19933      */
19934     
19935     maxColumns : 3,
19936    
19937     list : null, // the outermost div..
19938     innerLists : null, // the
19939     views : null,
19940     stores : null,
19941     // private
19942     loadingChildren : false,
19943     
19944     onRender : function(ct, position)
19945     {
19946         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19947         
19948         if(this.hiddenName){
19949             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19950                     'before', true);
19951             this.hiddenField.value =
19952                 this.hiddenValue !== undefined ? this.hiddenValue :
19953                 this.value !== undefined ? this.value : '';
19954
19955             // prevent input submission
19956             this.el.dom.removeAttribute('name');
19957              
19958              
19959         }
19960         
19961         if(Roo.isGecko){
19962             this.el.dom.setAttribute('autocomplete', 'off');
19963         }
19964
19965         var cls = 'x-combo-list';
19966
19967         this.list = new Roo.Layer({
19968             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19969         });
19970
19971         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19972         this.list.setWidth(lw);
19973         this.list.swallowEvent('mousewheel');
19974         this.assetHeight = 0;
19975
19976         if(this.title){
19977             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19978             this.assetHeight += this.header.getHeight();
19979         }
19980         this.innerLists = [];
19981         this.views = [];
19982         this.stores = [];
19983         for (var i =0 ; i < this.maxColumns; i++) {
19984             this.onRenderList( cls, i);
19985         }
19986         
19987         // always needs footer, as we are going to have an 'OK' button.
19988         this.footer = this.list.createChild({cls:cls+'-ft'});
19989         this.pageTb = new Roo.Toolbar(this.footer);  
19990         var _this = this;
19991         this.pageTb.add(  {
19992             
19993             text: 'Done',
19994             handler: function()
19995             {
19996                 _this.collapse();
19997             }
19998         });
19999         
20000         if ( this.allowBlank && !this.disableClear) {
20001             
20002             this.pageTb.add(new Roo.Toolbar.Fill(), {
20003                 cls: 'x-btn-icon x-btn-clear',
20004                 text: '&#160;',
20005                 handler: function()
20006                 {
20007                     _this.collapse();
20008                     _this.clearValue();
20009                     _this.onSelect(false, -1);
20010                 }
20011             });
20012         }
20013         if (this.footer) {
20014             this.assetHeight += this.footer.getHeight();
20015         }
20016         
20017     },
20018     onRenderList : function (  cls, i)
20019     {
20020         
20021         var lw = Math.floor(
20022                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20023         );
20024         
20025         this.list.setWidth(lw); // default to '1'
20026
20027         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20028         //il.on('mouseover', this.onViewOver, this, { list:  i });
20029         //il.on('mousemove', this.onViewMove, this, { list:  i });
20030         il.setWidth(lw);
20031         il.setStyle({ 'overflow-x' : 'hidden'});
20032
20033         if(!this.tpl){
20034             this.tpl = new Roo.Template({
20035                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20036                 isEmpty: function (value, allValues) {
20037                     //Roo.log(value);
20038                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20039                     return dl ? 'has-children' : 'no-children'
20040                 }
20041             });
20042         }
20043         
20044         var store  = this.store;
20045         if (i > 0) {
20046             store  = new Roo.data.SimpleStore({
20047                 //fields : this.store.reader.meta.fields,
20048                 reader : this.store.reader,
20049                 data : [ ]
20050             });
20051         }
20052         this.stores[i]  = store;
20053                   
20054         var view = this.views[i] = new Roo.View(
20055             il,
20056             this.tpl,
20057             {
20058                 singleSelect:true,
20059                 store: store,
20060                 selectedClass: this.selectedClass
20061             }
20062         );
20063         view.getEl().setWidth(lw);
20064         view.getEl().setStyle({
20065             position: i < 1 ? 'relative' : 'absolute',
20066             top: 0,
20067             left: (i * lw ) + 'px',
20068             display : i > 0 ? 'none' : 'block'
20069         });
20070         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20071         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20072         //view.on('click', this.onViewClick, this, { list : i });
20073
20074         store.on('beforeload', this.onBeforeLoad, this);
20075         store.on('load',  this.onLoad, this, { list  : i});
20076         store.on('loadexception', this.onLoadException, this);
20077
20078         // hide the other vies..
20079         
20080         
20081         
20082     },
20083       
20084     restrictHeight : function()
20085     {
20086         var mh = 0;
20087         Roo.each(this.innerLists, function(il,i) {
20088             var el = this.views[i].getEl();
20089             el.dom.style.height = '';
20090             var inner = el.dom;
20091             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20092             // only adjust heights on other ones..
20093             mh = Math.max(h, mh);
20094             if (i < 1) {
20095                 
20096                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20097                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20098                
20099             }
20100             
20101             
20102         }, this);
20103         
20104         this.list.beginUpdate();
20105         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20106         this.list.alignTo(this.el, this.listAlign);
20107         this.list.endUpdate();
20108         
20109     },
20110      
20111     
20112     // -- store handlers..
20113     // private
20114     onBeforeLoad : function()
20115     {
20116         if(!this.hasFocus){
20117             return;
20118         }
20119         this.innerLists[0].update(this.loadingText ?
20120                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20121         this.restrictHeight();
20122         this.selectedIndex = -1;
20123     },
20124     // private
20125     onLoad : function(a,b,c,d)
20126     {
20127         if (!this.loadingChildren) {
20128             // then we are loading the top level. - hide the children
20129             for (var i = 1;i < this.views.length; i++) {
20130                 this.views[i].getEl().setStyle({ display : 'none' });
20131             }
20132             var lw = Math.floor(
20133                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20134             );
20135         
20136              this.list.setWidth(lw); // default to '1'
20137
20138             
20139         }
20140         if(!this.hasFocus){
20141             return;
20142         }
20143         
20144         if(this.store.getCount() > 0) {
20145             this.expand();
20146             this.restrictHeight();   
20147         } else {
20148             this.onEmptyResults();
20149         }
20150         
20151         if (!this.loadingChildren) {
20152             this.selectActive();
20153         }
20154         /*
20155         this.stores[1].loadData([]);
20156         this.stores[2].loadData([]);
20157         this.views
20158         */    
20159     
20160         //this.el.focus();
20161     },
20162     
20163     
20164     // private
20165     onLoadException : function()
20166     {
20167         this.collapse();
20168         Roo.log(this.store.reader.jsonData);
20169         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20170             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20171         }
20172         
20173         
20174     },
20175     // no cleaning of leading spaces on blur here.
20176     cleanLeadingSpace : function(e) { },
20177     
20178
20179     onSelectChange : function (view, sels, opts )
20180     {
20181         var ix = view.getSelectedIndexes();
20182          
20183         if (opts.list > this.maxColumns - 2) {
20184             if (view.store.getCount()<  1) {
20185                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20186
20187             } else  {
20188                 if (ix.length) {
20189                     // used to clear ?? but if we are loading unselected 
20190                     this.setFromData(view.store.getAt(ix[0]).data);
20191                 }
20192                 
20193             }
20194             
20195             return;
20196         }
20197         
20198         if (!ix.length) {
20199             // this get's fired when trigger opens..
20200            // this.setFromData({});
20201             var str = this.stores[opts.list+1];
20202             str.data.clear(); // removeall wihtout the fire events..
20203             return;
20204         }
20205         
20206         var rec = view.store.getAt(ix[0]);
20207          
20208         this.setFromData(rec.data);
20209         this.fireEvent('select', this, rec, ix[0]);
20210         
20211         var lw = Math.floor(
20212              (
20213                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20214              ) / this.maxColumns
20215         );
20216         this.loadingChildren = true;
20217         this.stores[opts.list+1].loadDataFromChildren( rec );
20218         this.loadingChildren = false;
20219         var dl = this.stores[opts.list+1]. getTotalCount();
20220         
20221         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20222         
20223         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20224         for (var i = opts.list+2; i < this.views.length;i++) {
20225             this.views[i].getEl().setStyle({ display : 'none' });
20226         }
20227         
20228         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20229         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20230         
20231         if (this.isLoading) {
20232            // this.selectActive(opts.list);
20233         }
20234          
20235     },
20236     
20237     
20238     
20239     
20240     onDoubleClick : function()
20241     {
20242         this.collapse(); //??
20243     },
20244     
20245      
20246     
20247     
20248     
20249     // private
20250     recordToStack : function(store, prop, value, stack)
20251     {
20252         var cstore = new Roo.data.SimpleStore({
20253             //fields : this.store.reader.meta.fields, // we need array reader.. for
20254             reader : this.store.reader,
20255             data : [ ]
20256         });
20257         var _this = this;
20258         var record  = false;
20259         var srec = false;
20260         if(store.getCount() < 1){
20261             return false;
20262         }
20263         store.each(function(r){
20264             if(r.data[prop] == value){
20265                 record = r;
20266             srec = r;
20267                 return false;
20268             }
20269             if (r.data.cn && r.data.cn.length) {
20270                 cstore.loadDataFromChildren( r);
20271                 var cret = _this.recordToStack(cstore, prop, value, stack);
20272                 if (cret !== false) {
20273                     record = cret;
20274                     srec = r;
20275                     return false;
20276                 }
20277             }
20278              
20279             return true;
20280         });
20281         if (record == false) {
20282             return false
20283         }
20284         stack.unshift(srec);
20285         return record;
20286     },
20287     
20288     /*
20289      * find the stack of stores that match our value.
20290      *
20291      * 
20292      */
20293     
20294     selectActive : function ()
20295     {
20296         // if store is not loaded, then we will need to wait for that to happen first.
20297         var stack = [];
20298         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20299         for (var i = 0; i < stack.length; i++ ) {
20300             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20301         }
20302         
20303     }
20304         
20305          
20306     
20307     
20308     
20309     
20310 });/*
20311  * Based on:
20312  * Ext JS Library 1.1.1
20313  * Copyright(c) 2006-2007, Ext JS, LLC.
20314  *
20315  * Originally Released Under LGPL - original licence link has changed is not relivant.
20316  *
20317  * Fork - LGPL
20318  * <script type="text/javascript">
20319  */
20320 /**
20321  * @class Roo.form.Checkbox
20322  * @extends Roo.form.Field
20323  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20324  * @constructor
20325  * Creates a new Checkbox
20326  * @param {Object} config Configuration options
20327  */
20328 Roo.form.Checkbox = function(config){
20329     Roo.form.Checkbox.superclass.constructor.call(this, config);
20330     this.addEvents({
20331         /**
20332          * @event check
20333          * Fires when the checkbox is checked or unchecked.
20334              * @param {Roo.form.Checkbox} this This checkbox
20335              * @param {Boolean} checked The new checked value
20336              */
20337         check : true
20338     });
20339 };
20340
20341 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20342     /**
20343      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20344      */
20345     focusClass : undefined,
20346     /**
20347      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20348      */
20349     fieldClass: "x-form-field",
20350     /**
20351      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20352      */
20353     checked: false,
20354     /**
20355      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20356      * {tag: "input", type: "checkbox", autocomplete: "off"})
20357      */
20358     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20359     /**
20360      * @cfg {String} boxLabel The text that appears beside the checkbox
20361      */
20362     boxLabel : "",
20363     /**
20364      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20365      */  
20366     inputValue : '1',
20367     /**
20368      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20369      */
20370      valueOff: '0', // value when not checked..
20371
20372     actionMode : 'viewEl', 
20373     //
20374     // private
20375     itemCls : 'x-menu-check-item x-form-item',
20376     groupClass : 'x-menu-group-item',
20377     inputType : 'hidden',
20378     
20379     
20380     inSetChecked: false, // check that we are not calling self...
20381     
20382     inputElement: false, // real input element?
20383     basedOn: false, // ????
20384     
20385     isFormField: true, // not sure where this is needed!!!!
20386
20387     onResize : function(){
20388         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20389         if(!this.boxLabel){
20390             this.el.alignTo(this.wrap, 'c-c');
20391         }
20392     },
20393
20394     initEvents : function(){
20395         Roo.form.Checkbox.superclass.initEvents.call(this);
20396         this.el.on("click", this.onClick,  this);
20397         this.el.on("change", this.onClick,  this);
20398     },
20399
20400
20401     getResizeEl : function(){
20402         return this.wrap;
20403     },
20404
20405     getPositionEl : function(){
20406         return this.wrap;
20407     },
20408
20409     // private
20410     onRender : function(ct, position){
20411         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20412         /*
20413         if(this.inputValue !== undefined){
20414             this.el.dom.value = this.inputValue;
20415         }
20416         */
20417         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20418         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20419         var viewEl = this.wrap.createChild({ 
20420             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20421         this.viewEl = viewEl;   
20422         this.wrap.on('click', this.onClick,  this); 
20423         
20424         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20425         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20426         
20427         
20428         
20429         if(this.boxLabel){
20430             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20431         //    viewEl.on('click', this.onClick,  this); 
20432         }
20433         //if(this.checked){
20434             this.setChecked(this.checked);
20435         //}else{
20436             //this.checked = this.el.dom;
20437         //}
20438
20439     },
20440
20441     // private
20442     initValue : Roo.emptyFn,
20443
20444     /**
20445      * Returns the checked state of the checkbox.
20446      * @return {Boolean} True if checked, else false
20447      */
20448     getValue : function(){
20449         if(this.el){
20450             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20451         }
20452         return this.valueOff;
20453         
20454     },
20455
20456         // private
20457     onClick : function(){ 
20458         if (this.disabled) {
20459             return;
20460         }
20461         this.setChecked(!this.checked);
20462
20463         //if(this.el.dom.checked != this.checked){
20464         //    this.setValue(this.el.dom.checked);
20465        // }
20466     },
20467
20468     /**
20469      * Sets the checked state of the checkbox.
20470      * On is always based on a string comparison between inputValue and the param.
20471      * @param {Boolean/String} value - the value to set 
20472      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20473      */
20474     setValue : function(v,suppressEvent){
20475         
20476         
20477         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20478         //if(this.el && this.el.dom){
20479         //    this.el.dom.checked = this.checked;
20480         //    this.el.dom.defaultChecked = this.checked;
20481         //}
20482         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20483         //this.fireEvent("check", this, this.checked);
20484     },
20485     // private..
20486     setChecked : function(state,suppressEvent)
20487     {
20488         if (this.inSetChecked) {
20489             this.checked = state;
20490             return;
20491         }
20492         
20493     
20494         if(this.wrap){
20495             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20496         }
20497         this.checked = state;
20498         if(suppressEvent !== true){
20499             this.fireEvent('check', this, state);
20500         }
20501         this.inSetChecked = true;
20502         this.el.dom.value = state ? this.inputValue : this.valueOff;
20503         this.inSetChecked = false;
20504         
20505     },
20506     // handle setting of hidden value by some other method!!?!?
20507     setFromHidden: function()
20508     {
20509         if(!this.el){
20510             return;
20511         }
20512         //console.log("SET FROM HIDDEN");
20513         //alert('setFrom hidden');
20514         this.setValue(this.el.dom.value);
20515     },
20516     
20517     onDestroy : function()
20518     {
20519         if(this.viewEl){
20520             Roo.get(this.viewEl).remove();
20521         }
20522          
20523         Roo.form.Checkbox.superclass.onDestroy.call(this);
20524     },
20525     
20526     setBoxLabel : function(str)
20527     {
20528         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20529     }
20530
20531 });/*
20532  * Based on:
20533  * Ext JS Library 1.1.1
20534  * Copyright(c) 2006-2007, Ext JS, LLC.
20535  *
20536  * Originally Released Under LGPL - original licence link has changed is not relivant.
20537  *
20538  * Fork - LGPL
20539  * <script type="text/javascript">
20540  */
20541  
20542 /**
20543  * @class Roo.form.Radio
20544  * @extends Roo.form.Checkbox
20545  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20546  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20547  * @constructor
20548  * Creates a new Radio
20549  * @param {Object} config Configuration options
20550  */
20551 Roo.form.Radio = function(){
20552     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20553 };
20554 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20555     inputType: 'radio',
20556
20557     /**
20558      * If this radio is part of a group, it will return the selected value
20559      * @return {String}
20560      */
20561     getGroupValue : function(){
20562         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20563     },
20564     
20565     
20566     onRender : function(ct, position){
20567         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20568         
20569         if(this.inputValue !== undefined){
20570             this.el.dom.value = this.inputValue;
20571         }
20572          
20573         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20574         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20575         //var viewEl = this.wrap.createChild({ 
20576         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20577         //this.viewEl = viewEl;   
20578         //this.wrap.on('click', this.onClick,  this); 
20579         
20580         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20581         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20582         
20583         
20584         
20585         if(this.boxLabel){
20586             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20587         //    viewEl.on('click', this.onClick,  this); 
20588         }
20589          if(this.checked){
20590             this.el.dom.checked =   'checked' ;
20591         }
20592          
20593     } 
20594     
20595     
20596 });Roo.rtf = {}; // namespace
20597 Roo.rtf.Hex = function(hex)
20598 {
20599     this.hexstr = hex;
20600 };
20601 Roo.rtf.Paragraph = function(opts)
20602 {
20603     this.content = []; ///??? is that used?
20604 };Roo.rtf.Span = function(opts)
20605 {
20606     this.value = opts.value;
20607 };
20608
20609 Roo.rtf.Group = function(parent)
20610 {
20611     // we dont want to acutally store parent - it will make debug a nightmare..
20612     this.content = [];
20613     this.cn  = [];
20614      
20615        
20616     
20617 };
20618
20619 Roo.rtf.Group.prototype = {
20620     ignorable : false,
20621     content: false,
20622     cn: false,
20623     addContent : function(node) {
20624         // could set styles...
20625         this.content.push(node);
20626     },
20627     addChild : function(cn)
20628     {
20629         this.cn.push(cn);
20630     },
20631     // only for images really...
20632     toDataURL : function()
20633     {
20634         var mimetype = false;
20635         switch(true) {
20636             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20637                 mimetype = "image/png";
20638                 break;
20639              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20640                 mimetype = "image/jpeg";
20641                 break;
20642             default :
20643                 return 'about:blank'; // ?? error?
20644         }
20645         
20646         
20647         var hexstring = this.content[this.content.length-1].value;
20648         
20649         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20650             return String.fromCharCode(parseInt(a, 16));
20651         }).join(""));
20652     }
20653     
20654 };
20655 // this looks like it's normally the {rtf{ .... }}
20656 Roo.rtf.Document = function()
20657 {
20658     // we dont want to acutally store parent - it will make debug a nightmare..
20659     this.rtlch  = [];
20660     this.content = [];
20661     this.cn = [];
20662     
20663 };
20664 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20665     addChild : function(cn)
20666     {
20667         this.cn.push(cn);
20668         switch(cn.type) {
20669             case 'rtlch': // most content seems to be inside this??
20670             case 'listtext':
20671             case 'shpinst':
20672                 this.rtlch.push(cn);
20673                 return;
20674             default:
20675                 this[cn.type] = cn;
20676         }
20677         
20678     },
20679     
20680     getElementsByType : function(type)
20681     {
20682         var ret =  [];
20683         this._getElementsByType(type, ret, this.cn, 'rtf');
20684         return ret;
20685     },
20686     _getElementsByType : function (type, ret, search_array, path)
20687     {
20688         search_array.forEach(function(n,i) {
20689             if (n.type == type) {
20690                 n.path = path + '/' + n.type + ':' + i;
20691                 ret.push(n);
20692             }
20693             if (n.cn.length > 0) {
20694                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20695             }
20696         },this);
20697     }
20698     
20699 });
20700  
20701 Roo.rtf.Ctrl = function(opts)
20702 {
20703     this.value = opts.value;
20704     this.param = opts.param;
20705 };
20706 /**
20707  *
20708  *
20709  * based on this https://github.com/iarna/rtf-parser
20710  * it's really only designed to extract pict from pasted RTF 
20711  *
20712  * usage:
20713  *
20714  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20715  *  
20716  *
20717  */
20718
20719  
20720
20721
20722
20723 Roo.rtf.Parser = function(text) {
20724     //super({objectMode: true})
20725     this.text = '';
20726     this.parserState = this.parseText;
20727     
20728     // these are for interpeter...
20729     this.doc = {};
20730     ///this.parserState = this.parseTop
20731     this.groupStack = [];
20732     this.hexStore = [];
20733     this.doc = false;
20734     
20735     this.groups = []; // where we put the return.
20736     
20737     for (var ii = 0; ii < text.length; ++ii) {
20738         ++this.cpos;
20739         
20740         if (text[ii] === '\n') {
20741             ++this.row;
20742             this.col = 1;
20743         } else {
20744             ++this.col;
20745         }
20746         this.parserState(text[ii]);
20747     }
20748     
20749     
20750     
20751 };
20752 Roo.rtf.Parser.prototype = {
20753     text : '', // string being parsed..
20754     controlWord : '',
20755     controlWordParam :  '',
20756     hexChar : '',
20757     doc : false,
20758     group: false,
20759     groupStack : false,
20760     hexStore : false,
20761     
20762     
20763     cpos : 0, 
20764     row : 1, // reportin?
20765     col : 1, //
20766
20767      
20768     push : function (el)
20769     {
20770         var m = 'cmd'+ el.type;
20771         if (typeof(this[m]) == 'undefined') {
20772             Roo.log('invalid cmd:' + el.type);
20773             return;
20774         }
20775         this[m](el);
20776         //Roo.log(el);
20777     },
20778     flushHexStore : function()
20779     {
20780         if (this.hexStore.length < 1) {
20781             return;
20782         }
20783         var hexstr = this.hexStore.map(
20784             function(cmd) {
20785                 return cmd.value;
20786         }).join('');
20787         
20788         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20789               
20790             
20791         this.hexStore.splice(0)
20792         
20793     },
20794     
20795     cmdgroupstart : function()
20796     {
20797         this.flushHexStore();
20798         if (this.group) {
20799             this.groupStack.push(this.group);
20800         }
20801          // parent..
20802         if (this.doc === false) {
20803             this.group = this.doc = new Roo.rtf.Document();
20804             return;
20805             
20806         }
20807         this.group = new Roo.rtf.Group(this.group);
20808     },
20809     cmdignorable : function()
20810     {
20811         this.flushHexStore();
20812         this.group.ignorable = true;
20813     },
20814     cmdendparagraph : function()
20815     {
20816         this.flushHexStore();
20817         this.group.addContent(new Roo.rtf.Paragraph());
20818     },
20819     cmdgroupend : function ()
20820     {
20821         this.flushHexStore();
20822         var endingGroup = this.group;
20823         
20824         
20825         this.group = this.groupStack.pop();
20826         if (this.group) {
20827             this.group.addChild(endingGroup);
20828         }
20829         
20830         
20831         
20832         var doc = this.group || this.doc;
20833         //if (endingGroup instanceof FontTable) {
20834         //  doc.fonts = endingGroup.table
20835         //} else if (endingGroup instanceof ColorTable) {
20836         //  doc.colors = endingGroup.table
20837         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20838         if (endingGroup.ignorable === false) {
20839             //code
20840             this.groups.push(endingGroup);
20841            // Roo.log( endingGroup );
20842         }
20843             //Roo.each(endingGroup.content, function(item)) {
20844             //    doc.addContent(item);
20845             //}
20846             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20847         //}
20848     },
20849     cmdtext : function (cmd)
20850     {
20851         this.flushHexStore();
20852         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20853             //this.group = this.doc
20854         }
20855         this.group.addContent(new Roo.rtf.Span(cmd));
20856     },
20857     cmdcontrolword : function (cmd)
20858     {
20859         this.flushHexStore();
20860         if (!this.group.type) {
20861             this.group.type = cmd.value;
20862             return;
20863         }
20864         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20865         // we actually don't care about ctrl words...
20866         return ;
20867         /*
20868         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20869         if (this[method]) {
20870             this[method](cmd.param)
20871         } else {
20872             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20873         }
20874         */
20875     },
20876     cmdhexchar : function(cmd) {
20877         this.hexStore.push(cmd);
20878     },
20879     cmderror : function(cmd) {
20880         throw new Exception (cmd.value);
20881     },
20882     
20883     /*
20884       _flush (done) {
20885         if (this.text !== '\u0000') this.emitText()
20886         done()
20887       }
20888       */
20889       
20890       
20891     parseText : function(c)
20892     {
20893         if (c === '\\') {
20894             this.parserState = this.parseEscapes;
20895         } else if (c === '{') {
20896             this.emitStartGroup();
20897         } else if (c === '}') {
20898             this.emitEndGroup();
20899         } else if (c === '\x0A' || c === '\x0D') {
20900             // cr/lf are noise chars
20901         } else {
20902             this.text += c;
20903         }
20904     },
20905     
20906     parseEscapes: function (c)
20907     {
20908         if (c === '\\' || c === '{' || c === '}') {
20909             this.text += c;
20910             this.parserState = this.parseText;
20911         } else {
20912             this.parserState = this.parseControlSymbol;
20913             this.parseControlSymbol(c);
20914         }
20915     },
20916     parseControlSymbol: function(c)
20917     {
20918         if (c === '~') {
20919             this.text += '\u00a0'; // nbsp
20920             this.parserState = this.parseText
20921         } else if (c === '-') {
20922              this.text += '\u00ad'; // soft hyphen
20923         } else if (c === '_') {
20924             this.text += '\u2011'; // non-breaking hyphen
20925         } else if (c === '*') {
20926             this.emitIgnorable();
20927             this.parserState = this.parseText;
20928         } else if (c === "'") {
20929             this.parserState = this.parseHexChar;
20930         } else if (c === '|') { // formula cacter
20931             this.emitFormula();
20932             this.parserState = this.parseText;
20933         } else if (c === ':') { // subentry in an index entry
20934             this.emitIndexSubEntry();
20935             this.parserState = this.parseText;
20936         } else if (c === '\x0a') {
20937             this.emitEndParagraph();
20938             this.parserState = this.parseText;
20939         } else if (c === '\x0d') {
20940             this.emitEndParagraph();
20941             this.parserState = this.parseText;
20942         } else {
20943             this.parserState = this.parseControlWord;
20944             this.parseControlWord(c);
20945         }
20946     },
20947     parseHexChar: function (c)
20948     {
20949         if (/^[A-Fa-f0-9]$/.test(c)) {
20950             this.hexChar += c;
20951             if (this.hexChar.length >= 2) {
20952               this.emitHexChar();
20953               this.parserState = this.parseText;
20954             }
20955             return;
20956         }
20957         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20958         this.parserState = this.parseText;
20959         
20960     },
20961     parseControlWord : function(c)
20962     {
20963         if (c === ' ') {
20964             this.emitControlWord();
20965             this.parserState = this.parseText;
20966         } else if (/^[-\d]$/.test(c)) {
20967             this.parserState = this.parseControlWordParam;
20968             this.controlWordParam += c;
20969         } else if (/^[A-Za-z]$/.test(c)) {
20970           this.controlWord += c;
20971         } else {
20972           this.emitControlWord();
20973           this.parserState = this.parseText;
20974           this.parseText(c);
20975         }
20976     },
20977     parseControlWordParam : function (c) {
20978         if (/^\d$/.test(c)) {
20979           this.controlWordParam += c;
20980         } else if (c === ' ') {
20981           this.emitControlWord();
20982           this.parserState = this.parseText;
20983         } else {
20984           this.emitControlWord();
20985           this.parserState = this.parseText;
20986           this.parseText(c);
20987         }
20988     },
20989     
20990     
20991     
20992     
20993     emitText : function () {
20994         if (this.text === '') {
20995             return;
20996         }
20997         this.push({
20998             type: 'text',
20999             value: this.text,
21000             pos: this.cpos,
21001             row: this.row,
21002             col: this.col
21003         });
21004         this.text = ''
21005     },
21006     emitControlWord : function ()
21007     {
21008         this.emitText();
21009         if (this.controlWord === '') {
21010             this.emitError('empty control word');
21011         } else {
21012             this.push({
21013                   type: 'controlword',
21014                   value: this.controlWord,
21015                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
21016                   pos: this.cpos,
21017                   row: this.row,
21018                   col: this.col
21019             });
21020         }
21021         this.controlWord = '';
21022         this.controlWordParam = '';
21023     },
21024     emitStartGroup : function ()
21025     {
21026         this.emitText();
21027         this.push({
21028             type: 'groupstart',
21029             pos: this.cpos,
21030             row: this.row,
21031             col: this.col
21032         });
21033     },
21034     emitEndGroup : function ()
21035     {
21036         this.emitText();
21037         this.push({
21038             type: 'groupend',
21039             pos: this.cpos,
21040             row: this.row,
21041             col: this.col
21042         });
21043     },
21044     emitIgnorable : function ()
21045     {
21046         this.emitText();
21047         this.push({
21048             type: 'ignorable',
21049             pos: this.cpos,
21050             row: this.row,
21051             col: this.col
21052         });
21053     },
21054     emitHexChar : function ()
21055     {
21056         this.emitText();
21057         this.push({
21058             type: 'hexchar',
21059             value: this.hexChar,
21060             pos: this.cpos,
21061             row: this.row,
21062             col: this.col
21063         });
21064         this.hexChar = ''
21065     },
21066     emitError : function (message)
21067     {
21068       this.emitText();
21069       this.push({
21070             type: 'error',
21071             value: message,
21072             row: this.row,
21073             col: this.col,
21074             char: this.cpos //,
21075             //stack: new Error().stack
21076         });
21077     },
21078     emitEndParagraph : function () {
21079         this.emitText();
21080         this.push({
21081             type: 'endparagraph',
21082             pos: this.cpos,
21083             row: this.row,
21084             col: this.col
21085         });
21086     }
21087      
21088 } ;
21089 Roo.htmleditor = {};
21090  
21091 /**
21092  * @class Roo.htmleditor.Filter
21093  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21094  * @cfg {DomElement} node The node to iterate and filter
21095  * @cfg {boolean|String|Array} tag Tags to replace 
21096  * @constructor
21097  * Create a new Filter.
21098  * @param {Object} config Configuration options
21099  */
21100
21101
21102
21103 Roo.htmleditor.Filter = function(cfg) {
21104     Roo.apply(this.cfg);
21105     // this does not actually call walk as it's really just a abstract class
21106 }
21107
21108
21109 Roo.htmleditor.Filter.prototype = {
21110     
21111     node: false,
21112     
21113     tag: false,
21114
21115     // overrride to do replace comments.
21116     replaceComment : false,
21117     
21118     // overrride to do replace or do stuff with tags..
21119     replaceTag : false,
21120     
21121     walk : function(dom)
21122     {
21123         Roo.each( Array.from(dom.childNodes), function( e ) {
21124             switch(true) {
21125                 
21126                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
21127                     this.replaceComment(e);
21128                     return;
21129                 
21130                 case e.nodeType != 1: //not a node.
21131                     return;
21132                 
21133                 case this.tag === true: // everything
21134                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21135                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21136                     if (this.replaceTag && false === this.replaceTag(e)) {
21137                         return;
21138                     }
21139                     if (e.hasChildNodes()) {
21140                         this.walk(e);
21141                     }
21142                     return;
21143                 
21144                 default:    // tags .. that do not match.
21145                     if (e.hasChildNodes()) {
21146                         this.walk(e);
21147                     }
21148             }
21149             
21150         }, this);
21151         
21152     }
21153 }; 
21154
21155 /**
21156  * @class Roo.htmleditor.FilterAttributes
21157  * clean attributes and  styles including http:// etc.. in attribute
21158  * @constructor
21159 * Run a new Attribute Filter
21160 * @param {Object} config Configuration options
21161  */
21162 Roo.htmleditor.FilterAttributes = function(cfg)
21163 {
21164     Roo.apply(this, cfg);
21165     this.attrib_black = this.attrib_black || [];
21166     this.attrib_white = this.attrib_white || [];
21167
21168     this.attrib_clean = this.attrib_clean || [];
21169     this.style_white = this.style_white || [];
21170     this.style_black = this.style_black || [];
21171     this.walk(cfg.node);
21172 }
21173
21174 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21175 {
21176     tag: true, // all tags
21177     
21178     attrib_black : false, // array
21179     attrib_clean : false,
21180     attrib_white : false,
21181
21182     style_white : false,
21183     style_black : false,
21184      
21185      
21186     replaceTag : function(node)
21187     {
21188         if (!node.attributes || !node.attributes.length) {
21189             return true;
21190         }
21191         
21192         for (var i = node.attributes.length-1; i > -1 ; i--) {
21193             var a = node.attributes[i];
21194             //console.log(a);
21195             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21196                 node.removeAttribute(a.name);
21197                 continue;
21198             }
21199             
21200             
21201             
21202             if (a.name.toLowerCase().substr(0,2)=='on')  {
21203                 node.removeAttribute(a.name);
21204                 continue;
21205             }
21206             
21207             
21208             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21209                 node.removeAttribute(a.name);
21210                 continue;
21211             }
21212             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21213                 this.cleanAttr(node,a.name,a.value); // fixme..
21214                 continue;
21215             }
21216             if (a.name == 'style') {
21217                 this.cleanStyle(node,a.name,a.value);
21218                 continue;
21219             }
21220             /// clean up MS crap..
21221             // tecnically this should be a list of valid class'es..
21222             
21223             
21224             if (a.name == 'class') {
21225                 if (a.value.match(/^Mso/)) {
21226                     node.removeAttribute('class');
21227                 }
21228                 
21229                 if (a.value.match(/^body$/)) {
21230                     node.removeAttribute('class');
21231                 }
21232                 continue;
21233             }
21234             
21235             
21236             // style cleanup!?
21237             // class cleanup?
21238             
21239         }
21240         return true; // clean children
21241     },
21242         
21243     cleanAttr: function(node, n,v)
21244     {
21245         
21246         if (v.match(/^\./) || v.match(/^\//)) {
21247             return;
21248         }
21249         if (v.match(/^(http|https):\/\//)
21250             || v.match(/^mailto:/) 
21251             || v.match(/^ftp:/)
21252             || v.match(/^data:/)
21253             ) {
21254             return;
21255         }
21256         if (v.match(/^#/)) {
21257             return;
21258         }
21259         if (v.match(/^\{/)) { // allow template editing.
21260             return;
21261         }
21262 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21263         node.removeAttribute(n);
21264         
21265     },
21266     cleanStyle : function(node,  n,v)
21267     {
21268         if (v.match(/expression/)) { //XSS?? should we even bother..
21269             node.removeAttribute(n);
21270             return;
21271         }
21272         
21273         var parts = v.split(/;/);
21274         var clean = [];
21275         
21276         Roo.each(parts, function(p) {
21277             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21278             if (!p.length) {
21279                 return true;
21280             }
21281             var l = p.split(':').shift().replace(/\s+/g,'');
21282             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21283             
21284             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21285                 return true;
21286             }
21287             //Roo.log()
21288             // only allow 'c whitelisted system attributes'
21289             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21290                 return true;
21291             }
21292             
21293             
21294             clean.push(p);
21295             return true;
21296         },this);
21297         if (clean.length) { 
21298             node.setAttribute(n, clean.join(';'));
21299         } else {
21300             node.removeAttribute(n);
21301         }
21302         
21303     }
21304         
21305         
21306         
21307     
21308 });/**
21309  * @class Roo.htmleditor.FilterBlack
21310  * remove blacklisted elements.
21311  * @constructor
21312  * Run a new Blacklisted Filter
21313  * @param {Object} config Configuration options
21314  */
21315
21316 Roo.htmleditor.FilterBlack = function(cfg)
21317 {
21318     Roo.apply(this, cfg);
21319     this.walk(cfg.node);
21320 }
21321
21322 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21323 {
21324     tag : true, // all elements.
21325    
21326     replaceTag : function(n)
21327     {
21328         n.parentNode.removeChild(n);
21329     }
21330 });
21331 /**
21332  * @class Roo.htmleditor.FilterComment
21333  * remove comments.
21334  * @constructor
21335 * Run a new Comments Filter
21336 * @param {Object} config Configuration options
21337  */
21338 Roo.htmleditor.FilterComment = function(cfg)
21339 {
21340     this.walk(cfg.node);
21341 }
21342
21343 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21344 {
21345   
21346     replaceComment : function(n)
21347     {
21348         n.parentNode.removeChild(n);
21349     }
21350 });/**
21351  * @class Roo.htmleditor.FilterKeepChildren
21352  * remove tags but keep children
21353  * @constructor
21354  * Run a new Keep Children Filter
21355  * @param {Object} config Configuration options
21356  */
21357
21358 Roo.htmleditor.FilterKeepChildren = function(cfg)
21359 {
21360     Roo.apply(this, cfg);
21361     if (this.tag === false) {
21362         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21363     }
21364     this.walk(cfg.node);
21365 }
21366
21367 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21368 {
21369     
21370   
21371     replaceTag : function(node)
21372     {
21373         // walk children...
21374         //Roo.log(node);
21375         var ar = Array.from(node.childNodes);
21376         //remove first..
21377         for (var i = 0; i < ar.length; i++) {
21378             if (ar[i].nodeType == 1) {
21379                 if (
21380                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21381                     || // array and it matches
21382                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21383                 ) {
21384                     this.replaceTag(ar[i]); // child is blacklisted as well...
21385                     continue;
21386                 }
21387             }
21388         }  
21389         ar = Array.from(node.childNodes);
21390         for (var i = 0; i < ar.length; i++) {
21391          
21392             node.removeChild(ar[i]);
21393             // what if we need to walk these???
21394             node.parentNode.insertBefore(ar[i], node);
21395             if (this.tag !== false) {
21396                 this.walk(ar[i]);
21397                 
21398             }
21399         }
21400         node.parentNode.removeChild(node);
21401         return false; // don't walk children
21402         
21403         
21404     }
21405 });/**
21406  * @class Roo.htmleditor.FilterParagraph
21407  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21408  * like on 'push' to remove the <p> tags and replace them with line breaks.
21409  * @constructor
21410  * Run a new Paragraph Filter
21411  * @param {Object} config Configuration options
21412  */
21413
21414 Roo.htmleditor.FilterParagraph = function(cfg)
21415 {
21416     // no need to apply config.
21417     this.walk(cfg.node);
21418 }
21419
21420 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21421 {
21422     
21423      
21424     tag : 'P',
21425     
21426      
21427     replaceTag : function(node)
21428     {
21429         
21430         if (node.childNodes.length == 1 &&
21431             node.childNodes[0].nodeType == 3 &&
21432             node.childNodes[0].textContent.trim().length < 1
21433             ) {
21434             // remove and replace with '<BR>';
21435             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21436             return false; // no need to walk..
21437         }
21438         var ar = Array.from(node.childNodes);
21439         for (var i = 0; i < ar.length; i++) {
21440             node.removeChild(ar[i]);
21441             // what if we need to walk these???
21442             node.parentNode.insertBefore(ar[i], node);
21443         }
21444         // now what about this?
21445         // <p> &nbsp; </p>
21446         
21447         // double BR.
21448         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21449         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21450         node.parentNode.removeChild(node);
21451         
21452         return false;
21453
21454     }
21455     
21456 });/**
21457  * @class Roo.htmleditor.FilterSpan
21458  * filter span's with no attributes out..
21459  * @constructor
21460  * Run a new Span Filter
21461  * @param {Object} config Configuration options
21462  */
21463
21464 Roo.htmleditor.FilterSpan = function(cfg)
21465 {
21466     // no need to apply config.
21467     this.walk(cfg.node);
21468 }
21469
21470 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21471 {
21472      
21473     tag : 'SPAN',
21474      
21475  
21476     replaceTag : function(node)
21477     {
21478         if (node.attributes && node.attributes.length > 0) {
21479             return true; // walk if there are any.
21480         }
21481         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21482         return false;
21483      
21484     }
21485     
21486 });/**
21487  * @class Roo.htmleditor.FilterTableWidth
21488   try and remove table width data - as that frequently messes up other stuff.
21489  * 
21490  *      was cleanTableWidths.
21491  *
21492  * Quite often pasting from word etc.. results in tables with column and widths.
21493  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21494  *
21495  * @constructor
21496  * Run a new Table Filter
21497  * @param {Object} config Configuration options
21498  */
21499
21500 Roo.htmleditor.FilterTableWidth = function(cfg)
21501 {
21502     // no need to apply config.
21503     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21504     this.walk(cfg.node);
21505 }
21506
21507 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21508 {
21509      
21510      
21511     
21512     replaceTag: function(node) {
21513         
21514         
21515       
21516         if (node.hasAttribute('width')) {
21517             node.removeAttribute('width');
21518         }
21519         
21520          
21521         if (node.hasAttribute("style")) {
21522             // pretty basic...
21523             
21524             var styles = node.getAttribute("style").split(";");
21525             var nstyle = [];
21526             Roo.each(styles, function(s) {
21527                 if (!s.match(/:/)) {
21528                     return;
21529                 }
21530                 var kv = s.split(":");
21531                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21532                     return;
21533                 }
21534                 // what ever is left... we allow.
21535                 nstyle.push(s);
21536             });
21537             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21538             if (!nstyle.length) {
21539                 node.removeAttribute('style');
21540             }
21541         }
21542         
21543         return true; // continue doing children..
21544     }
21545 });/**
21546  * @class Roo.htmleditor.FilterWord
21547  * try and clean up all the mess that Word generates.
21548  * 
21549  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21550  
21551  * @constructor
21552  * Run a new Span Filter
21553  * @param {Object} config Configuration options
21554  */
21555
21556 Roo.htmleditor.FilterWord = function(cfg)
21557 {
21558     // no need to apply config.
21559     this.walk(cfg.node);
21560 }
21561
21562 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21563 {
21564     tag: true,
21565      
21566     
21567     /**
21568      * Clean up MS wordisms...
21569      */
21570     replaceTag : function(node)
21571     {
21572          
21573         // no idea what this does - span with text, replaceds with just text.
21574         if(
21575                 node.nodeName == 'SPAN' &&
21576                 !node.hasAttributes() &&
21577                 node.childNodes.length == 1 &&
21578                 node.firstChild.nodeName == "#text"  
21579         ) {
21580             var textNode = node.firstChild;
21581             node.removeChild(textNode);
21582             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21583                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21584             }
21585             node.parentNode.insertBefore(textNode, node);
21586             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21587                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21588             }
21589             
21590             node.parentNode.removeChild(node);
21591             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21592         }
21593         
21594    
21595         
21596         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21597             node.parentNode.removeChild(node);
21598             return false; // dont do chidlren
21599         }
21600         //Roo.log(node.tagName);
21601         // remove - but keep children..
21602         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21603             //Roo.log('-- removed');
21604             while (node.childNodes.length) {
21605                 var cn = node.childNodes[0];
21606                 node.removeChild(cn);
21607                 node.parentNode.insertBefore(cn, node);
21608                 // move node to parent - and clean it..
21609                 this.replaceTag(cn);
21610             }
21611             node.parentNode.removeChild(node);
21612             /// no need to iterate chidlren = it's got none..
21613             //this.iterateChildren(node, this.cleanWord);
21614             return false; // no need to iterate children.
21615         }
21616         // clean styles
21617         if (node.className.length) {
21618             
21619             var cn = node.className.split(/\W+/);
21620             var cna = [];
21621             Roo.each(cn, function(cls) {
21622                 if (cls.match(/Mso[a-zA-Z]+/)) {
21623                     return;
21624                 }
21625                 cna.push(cls);
21626             });
21627             node.className = cna.length ? cna.join(' ') : '';
21628             if (!cna.length) {
21629                 node.removeAttribute("class");
21630             }
21631         }
21632         
21633         if (node.hasAttribute("lang")) {
21634             node.removeAttribute("lang");
21635         }
21636         
21637         if (node.hasAttribute("style")) {
21638             
21639             var styles = node.getAttribute("style").split(";");
21640             var nstyle = [];
21641             Roo.each(styles, function(s) {
21642                 if (!s.match(/:/)) {
21643                     return;
21644                 }
21645                 var kv = s.split(":");
21646                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21647                     return;
21648                 }
21649                 // what ever is left... we allow.
21650                 nstyle.push(s);
21651             });
21652             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21653             if (!nstyle.length) {
21654                 node.removeAttribute('style');
21655             }
21656         }
21657         return true; // do children
21658         
21659         
21660         
21661     }
21662 });
21663 /**
21664  * @class Roo.htmleditor.FilterStyleToTag
21665  * part of the word stuff... - certain 'styles' should be converted to tags.
21666  * eg.
21667  *   font-weight: bold -> bold
21668  *   ?? super / subscrit etc..
21669  * 
21670  * @constructor
21671 * Run a new style to tag filter.
21672 * @param {Object} config Configuration options
21673  */
21674 Roo.htmleditor.FilterStyleToTag = function(cfg)
21675 {
21676     
21677     this.tags = {
21678         B  : [ 'fontWeight' , 'bold'],
21679         I :  [ 'fontStyle' , 'italic'],
21680         //pre :  [ 'font-style' , 'italic'],
21681         // h1.. h6 ?? font-size?
21682         SUP : [ 'verticalAlign' , 'super' ],
21683         SUB : [ 'verticalAlign' , 'sub' ]
21684         
21685         
21686     };
21687     
21688     Roo.apply(this, cfg);
21689      
21690     
21691     this.walk(cfg.node);
21692     
21693     
21694     
21695 }
21696
21697
21698 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21699 {
21700     tag: true, // all tags
21701     
21702     tags : false,
21703     
21704     
21705     replaceTag : function(node)
21706     {
21707         
21708         
21709         if (node.getAttribute("style") === null) {
21710             return true;
21711         }
21712         var inject = [];
21713         for (var k in this.tags) {
21714             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21715                 inject.push(k);
21716                 node.style.removeProperty(this.tags[k][0]);
21717             }
21718         }
21719         if (!inject.length) {
21720             return true; 
21721         }
21722         var cn = Array.from(node.childNodes);
21723         var nn = node;
21724         Roo.each(inject, function(t) {
21725             var nc = node.ownerDocument.createElement(t);
21726             nn.appendChild(nc);
21727             nn = nc;
21728         });
21729         for(var i = 0;i < cn.length;cn++) {
21730             node.removeChild(cn[i]);
21731             nn.appendChild(cn[i]);
21732         }
21733         return true /// iterate thru
21734     }
21735     
21736 })/**
21737  * @class Roo.htmleditor.FilterLongBr
21738  * BR/BR/BR - keep a maximum of 2...
21739  * @constructor
21740  * Run a new Long BR Filter
21741  * @param {Object} config Configuration options
21742  */
21743
21744 Roo.htmleditor.FilterLongBr = function(cfg)
21745 {
21746     // no need to apply config.
21747     this.walk(cfg.node);
21748 }
21749
21750 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21751 {
21752     
21753      
21754     tag : 'BR',
21755     
21756      
21757     replaceTag : function(node)
21758     {
21759         
21760         var ps = node.nextSibling;
21761         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21762             ps = ps.nextSibling;
21763         }
21764         
21765         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21766             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21767             return false;
21768         }
21769         
21770         if (!ps || ps.nodeType != 1) {
21771             return false;
21772         }
21773         
21774         if (!ps || ps.tagName != 'BR') {
21775            
21776             return false;
21777         }
21778         
21779         
21780         
21781         
21782         
21783         if (!node.previousSibling) {
21784             return false;
21785         }
21786         var ps = node.previousSibling;
21787         
21788         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21789             ps = ps.previousSibling;
21790         }
21791         if (!ps || ps.nodeType != 1) {
21792             return false;
21793         }
21794         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21795         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21796             return false;
21797         }
21798         
21799         node.parentNode.removeChild(node); // remove me...
21800         
21801         return false; // no need to do children
21802
21803     }
21804     
21805 }); 
21806
21807 /**
21808  * @class Roo.htmleditor.FilterBlock
21809  * removes id / data-block and contenteditable that are associated with blocks
21810  * usage should be done on a cloned copy of the dom
21811  * @constructor
21812 * Run a new Attribute Filter { node : xxxx }}
21813 * @param {Object} config Configuration options
21814  */
21815 Roo.htmleditor.FilterBlock = function(cfg)
21816 {
21817     Roo.apply(this, cfg);
21818     var qa = cfg.node.querySelectorAll;
21819     this.removeAttributes('data-block');
21820     this.removeAttributes('contenteditable');
21821     this.removeAttributes('id');
21822     
21823 }
21824
21825 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
21826 {
21827     node: true, // all tags
21828      
21829      
21830     removeAttributes : function(attr)
21831     {
21832         var ar = this.node.querySelectorAll('*[' + attr + ']');
21833         for (var i =0;i<ar.length;i++) {
21834             ar[i].removeAttribute(attr);
21835         }
21836     }
21837         
21838         
21839         
21840     
21841 });
21842 /***
21843  * This is based loosely on tinymce 
21844  * @class Roo.htmleditor.TidySerializer
21845  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21846  * @constructor
21847  * @method Serializer
21848  * @param {Object} settings Name/value settings object.
21849  */
21850
21851
21852 Roo.htmleditor.TidySerializer = function(settings)
21853 {
21854     Roo.apply(this, settings);
21855     
21856     this.writer = new Roo.htmleditor.TidyWriter(settings);
21857     
21858     
21859
21860 };
21861 Roo.htmleditor.TidySerializer.prototype = {
21862     
21863     /**
21864      * @param {boolean} inner do the inner of the node.
21865      */
21866     inner : false,
21867     
21868     writer : false,
21869     
21870     /**
21871     * Serializes the specified node into a string.
21872     *
21873     * @example
21874     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
21875     * @method serialize
21876     * @param {DomElement} node Node instance to serialize.
21877     * @return {String} String with HTML based on DOM tree.
21878     */
21879     serialize : function(node) {
21880         
21881         // = settings.validate;
21882         var writer = this.writer;
21883         var self  = this;
21884         this.handlers = {
21885             // #text
21886             3: function(node) {
21887                 
21888                 writer.text(node.nodeValue, node);
21889             },
21890             // #comment
21891             8: function(node) {
21892                 writer.comment(node.nodeValue);
21893             },
21894             // Processing instruction
21895             7: function(node) {
21896                 writer.pi(node.name, node.nodeValue);
21897             },
21898             // Doctype
21899             10: function(node) {
21900                 writer.doctype(node.nodeValue);
21901             },
21902             // CDATA
21903             4: function(node) {
21904                 writer.cdata(node.nodeValue);
21905             },
21906             // Document fragment
21907             11: function(node) {
21908                 node = node.firstChild;
21909                 if (!node) {
21910                     return;
21911                 }
21912                 while(node) {
21913                     self.walk(node);
21914                     node = node.nextSibling
21915                 }
21916             }
21917         };
21918         writer.reset();
21919         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
21920         return writer.getContent();
21921     },
21922
21923     walk: function(node)
21924     {
21925         var attrName, attrValue, sortedAttrs, i, l, elementRule,
21926             handler = this.handlers[node.nodeType];
21927             
21928         if (handler) {
21929             handler(node);
21930             return;
21931         }
21932     
21933         var name = node.nodeName;
21934         var isEmpty = node.childNodes.length < 1;
21935       
21936         var writer = this.writer;
21937         var attrs = node.attributes;
21938         // Sort attributes
21939         
21940         writer.start(node.nodeName, attrs, isEmpty, node);
21941         if (isEmpty) {
21942             return;
21943         }
21944         node = node.firstChild;
21945         if (!node) {
21946             writer.end(name);
21947             return;
21948         }
21949         while (node) {
21950             this.walk(node);
21951             node = node.nextSibling;
21952         }
21953         writer.end(name);
21954         
21955     
21956     }
21957     // Serialize element and treat all non elements as fragments
21958    
21959 }; 
21960
21961 /***
21962  * This is based loosely on tinymce 
21963  * @class Roo.htmleditor.TidyWriter
21964  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21965  *
21966  * Known issues?
21967  * - not tested much with 'PRE' formated elements.
21968  * 
21969  *
21970  *
21971  */
21972
21973 Roo.htmleditor.TidyWriter = function(settings)
21974 {
21975     
21976     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
21977     Roo.apply(this, settings);
21978     this.html = [];
21979     this.state = [];
21980      
21981     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
21982   
21983 }
21984 Roo.htmleditor.TidyWriter.prototype = {
21985
21986  
21987     state : false,
21988     
21989     indent :  '  ',
21990     
21991     // part of state...
21992     indentstr : '',
21993     in_pre: false,
21994     in_inline : false,
21995     last_inline : false,
21996     encode : false,
21997      
21998     
21999             /**
22000     * Writes the a start element such as <p id="a">.
22001     *
22002     * @method start
22003     * @param {String} name Name of the element.
22004     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22005     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22006     */
22007     start: function(name, attrs, empty, node)
22008     {
22009         var i, l, attr, value;
22010         
22011         // there are some situations where adding line break && indentation will not work. will not work.
22012         // <span / b / i ... formating?
22013         
22014         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22015         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22016         
22017         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22018         
22019         var add_lb = name == 'BR' ? false : in_inline;
22020         
22021         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22022             i_inline = false;
22023         }
22024
22025         var indentstr =  this.indentstr;
22026         
22027         // e_inline = elements that can be inline, but still allow \n before and after?
22028         // only 'BR' ??? any others?
22029         
22030         // ADD LINE BEFORE tage
22031         if (!this.in_pre) {
22032             if (in_inline) {
22033                 //code
22034                 if (name == 'BR') {
22035                     this.addLine();
22036                 } else if (this.lastElementEndsWS()) {
22037                     this.addLine();
22038                 } else{
22039                     // otherwise - no new line. (and dont indent.)
22040                     indentstr = '';
22041                 }
22042                 
22043             } else {
22044                 this.addLine();
22045             }
22046         } else {
22047             indentstr = '';
22048         }
22049         
22050         this.html.push(indentstr + '<', name.toLowerCase());
22051         
22052         if (attrs) {
22053             for (i = 0, l = attrs.length; i < l; i++) {
22054                 attr = attrs[i];
22055                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22056             }
22057         }
22058      
22059         if (empty) {
22060             if (is_short) {
22061                 this.html[this.html.length] = '/>';
22062             } else {
22063                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22064             }
22065             var e_inline = name == 'BR' ? false : this.in_inline;
22066             
22067             if (!e_inline && !this.in_pre) {
22068                 this.addLine();
22069             }
22070             return;
22071         
22072         }
22073         // not empty..
22074         this.html[this.html.length] = '>';
22075         
22076         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22077         /*
22078         if (!in_inline && !in_pre) {
22079             var cn = node.firstChild;
22080             while(cn) {
22081                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22082                     in_inline = true
22083                     break;
22084                 }
22085                 cn = cn.nextSibling;
22086             }
22087              
22088         }
22089         */
22090         
22091         
22092         this.pushState({
22093             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
22094             in_pre : in_pre,
22095             in_inline :  in_inline
22096         });
22097         // add a line after if we are not in a
22098         
22099         if (!in_inline && !in_pre) {
22100             this.addLine();
22101         }
22102         
22103             
22104          
22105         
22106     },
22107     
22108     lastElementEndsWS : function()
22109     {
22110         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22111         if (value === false) {
22112             return true;
22113         }
22114         return value.match(/\s+$/);
22115         
22116     },
22117     
22118     /**
22119      * Writes the a end element such as </p>.
22120      *
22121      * @method end
22122      * @param {String} name Name of the element.
22123      */
22124     end: function(name) {
22125         var value;
22126         this.popState();
22127         var indentstr = '';
22128         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22129         
22130         if (!this.in_pre && !in_inline) {
22131             this.addLine();
22132             indentstr  = this.indentstr;
22133         }
22134         this.html.push(indentstr + '</', name.toLowerCase(), '>');
22135         this.last_inline = in_inline;
22136         
22137         // pop the indent state..
22138     },
22139     /**
22140      * Writes a text node.
22141      *
22142      * In pre - we should not mess with the contents.
22143      * 
22144      *
22145      * @method text
22146      * @param {String} text String to write out.
22147      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22148      */
22149     text: function(text, node)
22150     {
22151         // if not in whitespace critical
22152         if (text.length < 1) {
22153             return;
22154         }
22155         if (this.in_pre) {
22156             this.html[this.html.length] =  text;
22157             return;   
22158         }
22159         
22160         if (this.in_inline) {
22161             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22162             if (text != ' ') {
22163                 text = text.replace(/\s+/,' ');  // all white space to single white space
22164                 
22165                     
22166                 // if next tag is '<BR>', then we can trim right..
22167                 if (node.nextSibling &&
22168                     node.nextSibling.nodeType == 1 &&
22169                     node.nextSibling.nodeName == 'BR' )
22170                 {
22171                     text = text.replace(/\s+$/g,'');
22172                 }
22173                 // if previous tag was a BR, we can also trim..
22174                 if (node.previousSibling &&
22175                     node.previousSibling.nodeType == 1 &&
22176                     node.previousSibling.nodeName == 'BR' )
22177                 {
22178                     text = this.indentstr +  text.replace(/^\s+/g,'');
22179                 }
22180                 if (text.match(/\n/)) {
22181                     text = text.replace(
22182                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22183                     );
22184                     // remoeve the last whitespace / line break.
22185                     text = text.replace(/\n\s+$/,'');
22186                 }
22187                 // repace long lines
22188                 
22189             }
22190              
22191             this.html[this.html.length] =  text;
22192             return;   
22193         }
22194         // see if previous element was a inline element.
22195         var indentstr = this.indentstr;
22196    
22197         text = text.replace(/\s+/g," "); // all whitespace into single white space.
22198         
22199         // should trim left?
22200         if (node.previousSibling &&
22201             node.previousSibling.nodeType == 1 &&
22202             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22203         {
22204             indentstr = '';
22205             
22206         } else {
22207             this.addLine();
22208             text = text.replace(/^\s+/,''); // trim left
22209           
22210         }
22211         // should trim right?
22212         if (node.nextSibling &&
22213             node.nextSibling.nodeType == 1 &&
22214             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22215         {
22216           // noop
22217             
22218         }  else {
22219             text = text.replace(/\s+$/,''); // trim right
22220         }
22221          
22222               
22223         
22224         
22225         
22226         if (text.length < 1) {
22227             return;
22228         }
22229         if (!text.match(/\n/)) {
22230             this.html.push(indentstr + text);
22231             return;
22232         }
22233         
22234         text = this.indentstr + text.replace(
22235             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22236         );
22237         // remoeve the last whitespace / line break.
22238         text = text.replace(/\s+$/,''); 
22239         
22240         this.html.push(text);
22241         
22242         // split and indent..
22243         
22244         
22245     },
22246     /**
22247      * Writes a cdata node such as <![CDATA[data]]>.
22248      *
22249      * @method cdata
22250      * @param {String} text String to write out inside the cdata.
22251      */
22252     cdata: function(text) {
22253         this.html.push('<![CDATA[', text, ']]>');
22254     },
22255     /**
22256     * Writes a comment node such as <!-- Comment -->.
22257     *
22258     * @method cdata
22259     * @param {String} text String to write out inside the comment.
22260     */
22261    comment: function(text) {
22262        this.html.push('<!--', text, '-->');
22263    },
22264     /**
22265      * Writes a PI node such as <?xml attr="value" ?>.
22266      *
22267      * @method pi
22268      * @param {String} name Name of the pi.
22269      * @param {String} text String to write out inside the pi.
22270      */
22271     pi: function(name, text) {
22272         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22273         this.indent != '' && this.html.push('\n');
22274     },
22275     /**
22276      * Writes a doctype node such as <!DOCTYPE data>.
22277      *
22278      * @method doctype
22279      * @param {String} text String to write out inside the doctype.
22280      */
22281     doctype: function(text) {
22282         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22283     },
22284     /**
22285      * Resets the internal buffer if one wants to reuse the writer.
22286      *
22287      * @method reset
22288      */
22289     reset: function() {
22290         this.html.length = 0;
22291         this.state = [];
22292         this.pushState({
22293             indentstr : '',
22294             in_pre : false, 
22295             in_inline : false
22296         })
22297     },
22298     /**
22299      * Returns the contents that got serialized.
22300      *
22301      * @method getContent
22302      * @return {String} HTML contents that got written down.
22303      */
22304     getContent: function() {
22305         return this.html.join('').replace(/\n$/, '');
22306     },
22307     
22308     pushState : function(cfg)
22309     {
22310         this.state.push(cfg);
22311         Roo.apply(this, cfg);
22312     },
22313     
22314     popState : function()
22315     {
22316         if (this.state.length < 1) {
22317             return; // nothing to push
22318         }
22319         var cfg = {
22320             in_pre: false,
22321             indentstr : ''
22322         };
22323         this.state.pop();
22324         if (this.state.length > 0) {
22325             cfg = this.state[this.state.length-1]; 
22326         }
22327         Roo.apply(this, cfg);
22328     },
22329     
22330     addLine: function()
22331     {
22332         if (this.html.length < 1) {
22333             return;
22334         }
22335         
22336         
22337         var value = this.html[this.html.length - 1];
22338         if (value.length > 0 && '\n' !== value) {
22339             this.html.push('\n');
22340         }
22341     }
22342     
22343     
22344 //'pre script noscript style textarea video audio iframe object code'
22345 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
22346 // inline 
22347 };
22348
22349 Roo.htmleditor.TidyWriter.inline_elements = [
22350         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22351         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP'
22352 ];
22353 Roo.htmleditor.TidyWriter.shortend_elements = [
22354     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22355     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22356 ];
22357
22358 Roo.htmleditor.TidyWriter.whitespace_elements = [
22359     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22360 ];/***
22361  * This is based loosely on tinymce 
22362  * @class Roo.htmleditor.TidyEntities
22363  * @static
22364  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22365  *
22366  * Not 100% sure this is actually used or needed.
22367  */
22368
22369 Roo.htmleditor.TidyEntities = {
22370     
22371     /**
22372      * initialize data..
22373      */
22374     init : function (){
22375      
22376         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22377        
22378     },
22379
22380
22381     buildEntitiesLookup: function(items, radix) {
22382         var i, chr, entity, lookup = {};
22383         if (!items) {
22384             return {};
22385         }
22386         items = typeof(items) == 'string' ? items.split(',') : items;
22387         radix = radix || 10;
22388         // Build entities lookup table
22389         for (i = 0; i < items.length; i += 2) {
22390             chr = String.fromCharCode(parseInt(items[i], radix));
22391             // Only add non base entities
22392             if (!this.baseEntities[chr]) {
22393                 entity = '&' + items[i + 1] + ';';
22394                 lookup[chr] = entity;
22395                 lookup[entity] = chr;
22396             }
22397         }
22398         return lookup;
22399         
22400     },
22401     
22402     asciiMap : {
22403             128: '€',
22404             130: '‚',
22405             131: 'ƒ',
22406             132: '„',
22407             133: '…',
22408             134: '†',
22409             135: '‡',
22410             136: 'ˆ',
22411             137: '‰',
22412             138: 'Š',
22413             139: '‹',
22414             140: 'Œ',
22415             142: 'Ž',
22416             145: '‘',
22417             146: '’',
22418             147: '“',
22419             148: '”',
22420             149: '•',
22421             150: '–',
22422             151: '—',
22423             152: '˜',
22424             153: '™',
22425             154: 'š',
22426             155: '›',
22427             156: 'œ',
22428             158: 'ž',
22429             159: 'Ÿ'
22430     },
22431     // Raw entities
22432     baseEntities : {
22433         '"': '&quot;',
22434         // Needs to be escaped since the YUI compressor would otherwise break the code
22435         '\'': '&#39;',
22436         '<': '&lt;',
22437         '>': '&gt;',
22438         '&': '&amp;',
22439         '`': '&#96;'
22440     },
22441     // Reverse lookup table for raw entities
22442     reverseEntities : {
22443         '&lt;': '<',
22444         '&gt;': '>',
22445         '&amp;': '&',
22446         '&quot;': '"',
22447         '&apos;': '\''
22448     },
22449     
22450     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22451     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22452     rawCharsRegExp : /[<>&\"\']/g,
22453     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22454     namedEntities  : false,
22455     namedEntitiesData : [ 
22456         '50',
22457         'nbsp',
22458         '51',
22459         'iexcl',
22460         '52',
22461         'cent',
22462         '53',
22463         'pound',
22464         '54',
22465         'curren',
22466         '55',
22467         'yen',
22468         '56',
22469         'brvbar',
22470         '57',
22471         'sect',
22472         '58',
22473         'uml',
22474         '59',
22475         'copy',
22476         '5a',
22477         'ordf',
22478         '5b',
22479         'laquo',
22480         '5c',
22481         'not',
22482         '5d',
22483         'shy',
22484         '5e',
22485         'reg',
22486         '5f',
22487         'macr',
22488         '5g',
22489         'deg',
22490         '5h',
22491         'plusmn',
22492         '5i',
22493         'sup2',
22494         '5j',
22495         'sup3',
22496         '5k',
22497         'acute',
22498         '5l',
22499         'micro',
22500         '5m',
22501         'para',
22502         '5n',
22503         'middot',
22504         '5o',
22505         'cedil',
22506         '5p',
22507         'sup1',
22508         '5q',
22509         'ordm',
22510         '5r',
22511         'raquo',
22512         '5s',
22513         'frac14',
22514         '5t',
22515         'frac12',
22516         '5u',
22517         'frac34',
22518         '5v',
22519         'iquest',
22520         '60',
22521         'Agrave',
22522         '61',
22523         'Aacute',
22524         '62',
22525         'Acirc',
22526         '63',
22527         'Atilde',
22528         '64',
22529         'Auml',
22530         '65',
22531         'Aring',
22532         '66',
22533         'AElig',
22534         '67',
22535         'Ccedil',
22536         '68',
22537         'Egrave',
22538         '69',
22539         'Eacute',
22540         '6a',
22541         'Ecirc',
22542         '6b',
22543         'Euml',
22544         '6c',
22545         'Igrave',
22546         '6d',
22547         'Iacute',
22548         '6e',
22549         'Icirc',
22550         '6f',
22551         'Iuml',
22552         '6g',
22553         'ETH',
22554         '6h',
22555         'Ntilde',
22556         '6i',
22557         'Ograve',
22558         '6j',
22559         'Oacute',
22560         '6k',
22561         'Ocirc',
22562         '6l',
22563         'Otilde',
22564         '6m',
22565         'Ouml',
22566         '6n',
22567         'times',
22568         '6o',
22569         'Oslash',
22570         '6p',
22571         'Ugrave',
22572         '6q',
22573         'Uacute',
22574         '6r',
22575         'Ucirc',
22576         '6s',
22577         'Uuml',
22578         '6t',
22579         'Yacute',
22580         '6u',
22581         'THORN',
22582         '6v',
22583         'szlig',
22584         '70',
22585         'agrave',
22586         '71',
22587         'aacute',
22588         '72',
22589         'acirc',
22590         '73',
22591         'atilde',
22592         '74',
22593         'auml',
22594         '75',
22595         'aring',
22596         '76',
22597         'aelig',
22598         '77',
22599         'ccedil',
22600         '78',
22601         'egrave',
22602         '79',
22603         'eacute',
22604         '7a',
22605         'ecirc',
22606         '7b',
22607         'euml',
22608         '7c',
22609         'igrave',
22610         '7d',
22611         'iacute',
22612         '7e',
22613         'icirc',
22614         '7f',
22615         'iuml',
22616         '7g',
22617         'eth',
22618         '7h',
22619         'ntilde',
22620         '7i',
22621         'ograve',
22622         '7j',
22623         'oacute',
22624         '7k',
22625         'ocirc',
22626         '7l',
22627         'otilde',
22628         '7m',
22629         'ouml',
22630         '7n',
22631         'divide',
22632         '7o',
22633         'oslash',
22634         '7p',
22635         'ugrave',
22636         '7q',
22637         'uacute',
22638         '7r',
22639         'ucirc',
22640         '7s',
22641         'uuml',
22642         '7t',
22643         'yacute',
22644         '7u',
22645         'thorn',
22646         '7v',
22647         'yuml',
22648         'ci',
22649         'fnof',
22650         'sh',
22651         'Alpha',
22652         'si',
22653         'Beta',
22654         'sj',
22655         'Gamma',
22656         'sk',
22657         'Delta',
22658         'sl',
22659         'Epsilon',
22660         'sm',
22661         'Zeta',
22662         'sn',
22663         'Eta',
22664         'so',
22665         'Theta',
22666         'sp',
22667         'Iota',
22668         'sq',
22669         'Kappa',
22670         'sr',
22671         'Lambda',
22672         'ss',
22673         'Mu',
22674         'st',
22675         'Nu',
22676         'su',
22677         'Xi',
22678         'sv',
22679         'Omicron',
22680         't0',
22681         'Pi',
22682         't1',
22683         'Rho',
22684         't3',
22685         'Sigma',
22686         't4',
22687         'Tau',
22688         't5',
22689         'Upsilon',
22690         't6',
22691         'Phi',
22692         't7',
22693         'Chi',
22694         't8',
22695         'Psi',
22696         't9',
22697         'Omega',
22698         'th',
22699         'alpha',
22700         'ti',
22701         'beta',
22702         'tj',
22703         'gamma',
22704         'tk',
22705         'delta',
22706         'tl',
22707         'epsilon',
22708         'tm',
22709         'zeta',
22710         'tn',
22711         'eta',
22712         'to',
22713         'theta',
22714         'tp',
22715         'iota',
22716         'tq',
22717         'kappa',
22718         'tr',
22719         'lambda',
22720         'ts',
22721         'mu',
22722         'tt',
22723         'nu',
22724         'tu',
22725         'xi',
22726         'tv',
22727         'omicron',
22728         'u0',
22729         'pi',
22730         'u1',
22731         'rho',
22732         'u2',
22733         'sigmaf',
22734         'u3',
22735         'sigma',
22736         'u4',
22737         'tau',
22738         'u5',
22739         'upsilon',
22740         'u6',
22741         'phi',
22742         'u7',
22743         'chi',
22744         'u8',
22745         'psi',
22746         'u9',
22747         'omega',
22748         'uh',
22749         'thetasym',
22750         'ui',
22751         'upsih',
22752         'um',
22753         'piv',
22754         '812',
22755         'bull',
22756         '816',
22757         'hellip',
22758         '81i',
22759         'prime',
22760         '81j',
22761         'Prime',
22762         '81u',
22763         'oline',
22764         '824',
22765         'frasl',
22766         '88o',
22767         'weierp',
22768         '88h',
22769         'image',
22770         '88s',
22771         'real',
22772         '892',
22773         'trade',
22774         '89l',
22775         'alefsym',
22776         '8cg',
22777         'larr',
22778         '8ch',
22779         'uarr',
22780         '8ci',
22781         'rarr',
22782         '8cj',
22783         'darr',
22784         '8ck',
22785         'harr',
22786         '8dl',
22787         'crarr',
22788         '8eg',
22789         'lArr',
22790         '8eh',
22791         'uArr',
22792         '8ei',
22793         'rArr',
22794         '8ej',
22795         'dArr',
22796         '8ek',
22797         'hArr',
22798         '8g0',
22799         'forall',
22800         '8g2',
22801         'part',
22802         '8g3',
22803         'exist',
22804         '8g5',
22805         'empty',
22806         '8g7',
22807         'nabla',
22808         '8g8',
22809         'isin',
22810         '8g9',
22811         'notin',
22812         '8gb',
22813         'ni',
22814         '8gf',
22815         'prod',
22816         '8gh',
22817         'sum',
22818         '8gi',
22819         'minus',
22820         '8gn',
22821         'lowast',
22822         '8gq',
22823         'radic',
22824         '8gt',
22825         'prop',
22826         '8gu',
22827         'infin',
22828         '8h0',
22829         'ang',
22830         '8h7',
22831         'and',
22832         '8h8',
22833         'or',
22834         '8h9',
22835         'cap',
22836         '8ha',
22837         'cup',
22838         '8hb',
22839         'int',
22840         '8hk',
22841         'there4',
22842         '8hs',
22843         'sim',
22844         '8i5',
22845         'cong',
22846         '8i8',
22847         'asymp',
22848         '8j0',
22849         'ne',
22850         '8j1',
22851         'equiv',
22852         '8j4',
22853         'le',
22854         '8j5',
22855         'ge',
22856         '8k2',
22857         'sub',
22858         '8k3',
22859         'sup',
22860         '8k4',
22861         'nsub',
22862         '8k6',
22863         'sube',
22864         '8k7',
22865         'supe',
22866         '8kl',
22867         'oplus',
22868         '8kn',
22869         'otimes',
22870         '8l5',
22871         'perp',
22872         '8m5',
22873         'sdot',
22874         '8o8',
22875         'lceil',
22876         '8o9',
22877         'rceil',
22878         '8oa',
22879         'lfloor',
22880         '8ob',
22881         'rfloor',
22882         '8p9',
22883         'lang',
22884         '8pa',
22885         'rang',
22886         '9ea',
22887         'loz',
22888         '9j0',
22889         'spades',
22890         '9j3',
22891         'clubs',
22892         '9j5',
22893         'hearts',
22894         '9j6',
22895         'diams',
22896         'ai',
22897         'OElig',
22898         'aj',
22899         'oelig',
22900         'b0',
22901         'Scaron',
22902         'b1',
22903         'scaron',
22904         'bo',
22905         'Yuml',
22906         'm6',
22907         'circ',
22908         'ms',
22909         'tilde',
22910         '802',
22911         'ensp',
22912         '803',
22913         'emsp',
22914         '809',
22915         'thinsp',
22916         '80c',
22917         'zwnj',
22918         '80d',
22919         'zwj',
22920         '80e',
22921         'lrm',
22922         '80f',
22923         'rlm',
22924         '80j',
22925         'ndash',
22926         '80k',
22927         'mdash',
22928         '80o',
22929         'lsquo',
22930         '80p',
22931         'rsquo',
22932         '80q',
22933         'sbquo',
22934         '80s',
22935         'ldquo',
22936         '80t',
22937         'rdquo',
22938         '80u',
22939         'bdquo',
22940         '810',
22941         'dagger',
22942         '811',
22943         'Dagger',
22944         '81g',
22945         'permil',
22946         '81p',
22947         'lsaquo',
22948         '81q',
22949         'rsaquo',
22950         '85c',
22951         'euro'
22952     ],
22953
22954          
22955     /**
22956      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
22957      *
22958      * @method encodeRaw
22959      * @param {String} text Text to encode.
22960      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22961      * @return {String} Entity encoded text.
22962      */
22963     encodeRaw: function(text, attr)
22964     {
22965         var t = this;
22966         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
22967             return t.baseEntities[chr] || chr;
22968         });
22969     },
22970     /**
22971      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
22972      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
22973      * and is exposed as the DOMUtils.encode function.
22974      *
22975      * @method encodeAllRaw
22976      * @param {String} text Text to encode.
22977      * @return {String} Entity encoded text.
22978      */
22979     encodeAllRaw: function(text) {
22980         var t = this;
22981         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
22982             return t.baseEntities[chr] || chr;
22983         });
22984     },
22985     /**
22986      * Encodes the specified string using numeric entities. The core entities will be
22987      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
22988      *
22989      * @method encodeNumeric
22990      * @param {String} text Text to encode.
22991      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22992      * @return {String} Entity encoded text.
22993      */
22994     encodeNumeric: function(text, attr) {
22995         var t = this;
22996         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
22997             // Multi byte sequence convert it to a single entity
22998             if (chr.length > 1) {
22999                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
23000             }
23001             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23002         });
23003     },
23004     /**
23005      * Encodes the specified string using named entities. The core entities will be encoded
23006      * as named ones but all non lower ascii characters will be encoded into named entities.
23007      *
23008      * @method encodeNamed
23009      * @param {String} text Text to encode.
23010      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23011      * @param {Object} entities Optional parameter with entities to use.
23012      * @return {String} Entity encoded text.
23013      */
23014     encodeNamed: function(text, attr, entities) {
23015         var t = this;
23016         entities = entities || this.namedEntities;
23017         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23018             return t.baseEntities[chr] || entities[chr] || chr;
23019         });
23020     },
23021     /**
23022      * Returns an encode function based on the name(s) and it's optional entities.
23023      *
23024      * @method getEncodeFunc
23025      * @param {String} name Comma separated list of encoders for example named,numeric.
23026      * @param {String} entities Optional parameter with entities to use instead of the built in set.
23027      * @return {function} Encode function to be used.
23028      */
23029     getEncodeFunc: function(name, entities) {
23030         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23031         var t = this;
23032         function encodeNamedAndNumeric(text, attr) {
23033             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23034                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23035             });
23036         }
23037
23038         function encodeCustomNamed(text, attr) {
23039             return t.encodeNamed(text, attr, entities);
23040         }
23041         // Replace + with , to be compatible with previous TinyMCE versions
23042         name = this.makeMap(name.replace(/\+/g, ','));
23043         // Named and numeric encoder
23044         if (name.named && name.numeric) {
23045             return this.encodeNamedAndNumeric;
23046         }
23047         // Named encoder
23048         if (name.named) {
23049             // Custom names
23050             if (entities) {
23051                 return encodeCustomNamed;
23052             }
23053             return this.encodeNamed;
23054         }
23055         // Numeric
23056         if (name.numeric) {
23057             return this.encodeNumeric;
23058         }
23059         // Raw encoder
23060         return this.encodeRaw;
23061     },
23062     /**
23063      * Decodes the specified string, this will replace entities with raw UTF characters.
23064      *
23065      * @method decode
23066      * @param {String} text Text to entity decode.
23067      * @return {String} Entity decoded string.
23068      */
23069     decode: function(text)
23070     {
23071         var  t = this;
23072         return text.replace(this.entityRegExp, function(all, numeric) {
23073             if (numeric) {
23074                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23075                 // Support upper UTF
23076                 if (numeric > 65535) {
23077                     numeric -= 65536;
23078                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23079                 }
23080                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23081             }
23082             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23083         });
23084     },
23085     nativeDecode : function (text) {
23086         return text;
23087     },
23088     makeMap : function (items, delim, map) {
23089                 var i;
23090                 items = items || [];
23091                 delim = delim || ',';
23092                 if (typeof items == "string") {
23093                         items = items.split(delim);
23094                 }
23095                 map = map || {};
23096                 i = items.length;
23097                 while (i--) {
23098                         map[items[i]] = {};
23099                 }
23100                 return map;
23101         }
23102 };
23103     
23104     
23105     
23106 Roo.htmleditor.TidyEntities.init();
23107 /**
23108  * @class Roo.htmleditor.KeyEnter
23109  * Handle Enter press..
23110  * @cfg {Roo.HtmlEditorCore} core the editor.
23111  * @constructor
23112  * Create a new Filter.
23113  * @param {Object} config Configuration options
23114  */
23115
23116
23117
23118
23119
23120 Roo.htmleditor.KeyEnter = function(cfg) {
23121     Roo.apply(this, cfg);
23122     // this does not actually call walk as it's really just a abstract class
23123  
23124     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23125 }
23126
23127 //Roo.htmleditor.KeyEnter.i = 0;
23128
23129
23130 Roo.htmleditor.KeyEnter.prototype = {
23131     
23132     core : false,
23133     
23134     keypress : function(e)
23135     {
23136         if (e.charCode != 13 && e.charCode != 10) {
23137             Roo.log([e.charCode,e]);
23138             return true;
23139         }
23140         e.preventDefault();
23141         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23142         var doc = this.core.doc;
23143           //add a new line
23144        
23145     
23146         var sel = this.core.getSelection();
23147         var range = sel.getRangeAt(0);
23148         var n = range.commonAncestorContainer;
23149         var pc = range.closest([ 'ol', 'ul']);
23150         var pli = range.closest('li');
23151         if (!pc || e.ctrlKey) {
23152             sel.insertNode('br', 'after'); 
23153          
23154             this.core.undoManager.addEvent();
23155             this.core.fireEditorEvent(e);
23156             return false;
23157         }
23158         
23159         // deal with <li> insetion
23160         if (pli.innerText.trim() == '' &&
23161             pli.previousSibling &&
23162             pli.previousSibling.nodeName == 'LI' &&
23163             pli.previousSibling.innerText.trim() ==  '') {
23164             pli.parentNode.removeChild(pli.previousSibling);
23165             sel.cursorAfter(pc);
23166             this.core.undoManager.addEvent();
23167             this.core.fireEditorEvent(e);
23168             return false;
23169         }
23170     
23171         var li = doc.createElement('LI');
23172         li.innerHTML = '&nbsp;';
23173         if (!pli || !pli.firstSibling) {
23174             pc.appendChild(li);
23175         } else {
23176             pli.parentNode.insertBefore(li, pli.firstSibling);
23177         }
23178         sel.cursorText (li.firstChild);
23179       
23180         this.core.undoManager.addEvent();
23181         this.core.fireEditorEvent(e);
23182
23183         return false;
23184         
23185     
23186         
23187         
23188          
23189     }
23190 };
23191      
23192 /**
23193  * @class Roo.htmleditor.Block
23194  * Base class for html editor blocks - do not use it directly .. extend it..
23195  * @cfg {DomElement} node The node to apply stuff to.
23196  * @cfg {String} friendly_name the name that appears in the context bar about this block
23197  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23198  
23199  * @constructor
23200  * Create a new Filter.
23201  * @param {Object} config Configuration options
23202  */
23203
23204 Roo.htmleditor.Block  = function(cfg)
23205 {
23206     // do nothing .. should not be called really.
23207 }
23208 /**
23209  * factory method to get the block from an element (using cache if necessary)
23210  * @static
23211  * @param {HtmlElement} the dom element
23212  */
23213 Roo.htmleditor.Block.factory = function(node)
23214 {
23215     var cc = Roo.htmleditor.Block.cache;
23216     var id = Roo.get(node).id;
23217     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23218         Roo.htmleditor.Block.cache[id].readElement(node);
23219         return Roo.htmleditor.Block.cache[id];
23220     }
23221     var db  = node.getAttribute('data-block');
23222     if (!db) {
23223         db = node.nodeName.toLowerCase().toUpperCaseFirst();
23224     }
23225     var cls = Roo.htmleditor['Block' + db];
23226     if (typeof(cls) == 'undefined') {
23227         //Roo.log(node.getAttribute('data-block'));
23228         Roo.log("OOps missing block : " + 'Block' + db);
23229         return false;
23230     }
23231     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23232     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
23233 };
23234
23235 /**
23236  * initalize all Elements from content that are 'blockable'
23237  * @static
23238  * @param the body element
23239  */
23240 Roo.htmleditor.Block.initAll = function(body, type)
23241 {
23242     if (typeof(type) == 'undefined') {
23243         var ia = Roo.htmleditor.Block.initAll;
23244         ia(body,'table');
23245         ia(body,'td');
23246         ia(body,'figure');
23247         return;
23248     }
23249     Roo.each(Roo.get(body).query(type), function(e) {
23250         Roo.htmleditor.Block.factory(e);    
23251     },this);
23252 };
23253 // question goes here... do we need to clear out this cache sometimes?
23254 // or show we make it relivant to the htmleditor.
23255 Roo.htmleditor.Block.cache = {};
23256
23257 Roo.htmleditor.Block.prototype = {
23258     
23259     node : false,
23260     
23261      // used by context menu
23262     friendly_name : 'Based Block',
23263     
23264     // text for button to delete this element
23265     deleteTitle : false,
23266     
23267     context : false,
23268     /**
23269      * Update a node with values from this object
23270      * @param {DomElement} node
23271      */
23272     updateElement : function(node)
23273     {
23274         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23275     },
23276      /**
23277      * convert to plain HTML for calling insertAtCursor..
23278      */
23279     toHTML : function()
23280     {
23281         return Roo.DomHelper.markup(this.toObject());
23282     },
23283     /**
23284      * used by readEleemnt to extract data from a node
23285      * may need improving as it's pretty basic
23286      
23287      * @param {DomElement} node
23288      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23289      * @param {String} attribute (use html - for contents, or style for using next param as style)
23290      * @param {String} style the style property - eg. text-align
23291      */
23292     getVal : function(node, tag, attr, style)
23293     {
23294         var n = node;
23295         if (tag !== true && n.tagName != tag.toUpperCase()) {
23296             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23297             // but kiss for now.
23298             n = node.getElementsByTagName(tag).item(0);
23299         }
23300         if (!n) {
23301             return '';
23302         }
23303         if (attr == 'html') {
23304             return n.innerHTML;
23305         }
23306         if (attr == 'style') {
23307             return n.style[style]; 
23308         }
23309         
23310         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23311             
23312     },
23313     /**
23314      * create a DomHelper friendly object - for use with 
23315      * Roo.DomHelper.markup / overwrite / etc..
23316      * (override this)
23317      */
23318     toObject : function()
23319     {
23320         return {};
23321     },
23322       /**
23323      * Read a node that has a 'data-block' property - and extract the values from it.
23324      * @param {DomElement} node - the node
23325      */
23326     readElement : function(node)
23327     {
23328         
23329     } 
23330     
23331     
23332 };
23333
23334  
23335
23336 /**
23337  * @class Roo.htmleditor.BlockFigure
23338  * Block that has an image and a figcaption
23339  * @cfg {String} image_src the url for the image
23340  * @cfg {String} align (left|right) alignment for the block default left
23341  * @cfg {String} caption the text to appear below  (and in the alt tag)
23342  * @cfg {String} caption_display (block|none) display or not the caption
23343  * @cfg {String|number} image_width the width of the image number or %?
23344  * @cfg {String|number} image_height the height of the image number or %?
23345  * 
23346  * @constructor
23347  * Create a new Filter.
23348  * @param {Object} config Configuration options
23349  */
23350
23351 Roo.htmleditor.BlockFigure = function(cfg)
23352 {
23353     if (cfg.node) {
23354         this.readElement(cfg.node);
23355         this.updateElement(cfg.node);
23356     }
23357     Roo.apply(this, cfg);
23358 }
23359 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23360  
23361     
23362     // setable values.
23363     image_src: '',
23364     align: 'center',
23365     caption : '',
23366     caption_display : 'block',
23367     width : '100%',
23368     cls : '',
23369     href: '',
23370     video_url : '',
23371     
23372     // margin: '2%', not used
23373     
23374     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
23375
23376     
23377     // used by context menu
23378     friendly_name : 'Image with caption',
23379     deleteTitle : "Delete Image and Caption",
23380     
23381     contextMenu : function(toolbar)
23382     {
23383         
23384         var block = function() {
23385             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23386         };
23387         
23388         
23389         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23390         
23391         var syncValue = toolbar.editorcore.syncValue;
23392         
23393         var fields = {};
23394         
23395         return [
23396              {
23397                 xtype : 'TextItem',
23398                 text : "Source: ",
23399                 xns : rooui.Toolbar  //Boostrap?
23400             },
23401             {
23402                 xtype : 'Button',
23403                 text: 'Change Image URL',
23404                  
23405                 listeners : {
23406                     click: function (btn, state)
23407                     {
23408                         var b = block();
23409                         
23410                         Roo.MessageBox.show({
23411                             title : "Image Source URL",
23412                             msg : "Enter the url for the image",
23413                             buttons: Roo.MessageBox.OKCANCEL,
23414                             fn: function(btn, val){
23415                                 if (btn != 'ok') {
23416                                     return;
23417                                 }
23418                                 b.image_src = val;
23419                                 b.updateElement();
23420                                 syncValue();
23421                                 toolbar.editorcore.onEditorEvent();
23422                             },
23423                             minWidth:250,
23424                             prompt:true,
23425                             //multiline: multiline,
23426                             modal : true,
23427                             value : b.image_src
23428                         });
23429                     }
23430                 },
23431                 xns : rooui.Toolbar
23432             },
23433          
23434             {
23435                 xtype : 'Button',
23436                 text: 'Change Link URL',
23437                  
23438                 listeners : {
23439                     click: function (btn, state)
23440                     {
23441                         var b = block();
23442                         
23443                         Roo.MessageBox.show({
23444                             title : "Link URL",
23445                             msg : "Enter the url for the link - leave blank to have no link",
23446                             buttons: Roo.MessageBox.OKCANCEL,
23447                             fn: function(btn, val){
23448                                 if (btn != 'ok') {
23449                                     return;
23450                                 }
23451                                 b.href = val;
23452                                 b.updateElement();
23453                                 syncValue();
23454                                 toolbar.editorcore.onEditorEvent();
23455                             },
23456                             minWidth:250,
23457                             prompt:true,
23458                             //multiline: multiline,
23459                             modal : true,
23460                             value : b.href
23461                         });
23462                     }
23463                 },
23464                 xns : rooui.Toolbar
23465             },
23466             {
23467                 xtype : 'Button',
23468                 text: 'Show Video URL',
23469                  
23470                 listeners : {
23471                     click: function (btn, state)
23472                     {
23473                         Roo.MessageBox.alert("Video URL",
23474                             block().video_url == '' ? 'This image is not linked ot a video' :
23475                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23476                     }
23477                 },
23478                 xns : rooui.Toolbar
23479             },
23480             
23481             
23482             {
23483                 xtype : 'TextItem',
23484                 text : "Width: ",
23485                 xns : rooui.Toolbar  //Boostrap?
23486             },
23487             {
23488                 xtype : 'ComboBox',
23489                 allowBlank : false,
23490                 displayField : 'val',
23491                 editable : true,
23492                 listWidth : 100,
23493                 triggerAction : 'all',
23494                 typeAhead : true,
23495                 valueField : 'val',
23496                 width : 70,
23497                 name : 'width',
23498                 listeners : {
23499                     select : function (combo, r, index)
23500                     {
23501                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23502                         var b = block();
23503                         b.width = r.get('val');
23504                         b.updateElement();
23505                         syncValue();
23506                         toolbar.editorcore.onEditorEvent();
23507                     }
23508                 },
23509                 xns : rooui.form,
23510                 store : {
23511                     xtype : 'SimpleStore',
23512                     data : [
23513                         ['auto'],
23514                         ['50%'],
23515                         ['100%']
23516                     ],
23517                     fields : [ 'val'],
23518                     xns : Roo.data
23519                 }
23520             },
23521             {
23522                 xtype : 'TextItem',
23523                 text : "Align: ",
23524                 xns : rooui.Toolbar  //Boostrap?
23525             },
23526             {
23527                 xtype : 'ComboBox',
23528                 allowBlank : false,
23529                 displayField : 'val',
23530                 editable : true,
23531                 listWidth : 100,
23532                 triggerAction : 'all',
23533                 typeAhead : true,
23534                 valueField : 'val',
23535                 width : 70,
23536                 name : 'align',
23537                 listeners : {
23538                     select : function (combo, r, index)
23539                     {
23540                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23541                         var b = block();
23542                         b.align = r.get('val');
23543                         b.updateElement();
23544                         syncValue();
23545                         toolbar.editorcore.onEditorEvent();
23546                     }
23547                 },
23548                 xns : rooui.form,
23549                 store : {
23550                     xtype : 'SimpleStore',
23551                     data : [
23552                         ['left'],
23553                         ['right'],
23554                         ['center']
23555                     ],
23556                     fields : [ 'val'],
23557                     xns : Roo.data
23558                 }
23559             },
23560             
23561             
23562             {
23563                 xtype : 'Button',
23564                 text: 'Hide Caption',
23565                 name : 'caption_display',
23566                 pressed : false,
23567                 enableToggle : true,
23568                 setValue : function(v) {
23569                     this.toggle(v == 'block' ? false : true);
23570                 },
23571                 listeners : {
23572                     toggle: function (btn, state)
23573                     {
23574                         var b  = block();
23575                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
23576                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23577                         b.updateElement();
23578                         syncValue();
23579                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23580                         toolbar.editorcore.onEditorEvent();
23581                     }
23582                 },
23583                 xns : rooui.Toolbar
23584             }
23585         ];
23586         
23587     },
23588     /**
23589      * create a DomHelper friendly object - for use with
23590      * Roo.DomHelper.markup / overwrite / etc..
23591      */
23592     toObject : function()
23593     {
23594         var d = document.createElement('div');
23595         d.innerHTML = this.caption;
23596         
23597         var m = this.width == '50%' && this.align == 'center' ? '0 auto' : 0; 
23598         
23599         var img =   {
23600             tag : 'img',
23601             contenteditable : 'false',
23602             src : this.image_src,
23603             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
23604             style: {
23605                 width : 'auto',
23606                 'max-width': '100%',
23607                 margin : '0px' 
23608                 
23609                 
23610             }
23611         };
23612         /*
23613         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
23614                     '<a href="{2}">' + 
23615                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
23616                     '</a>' + 
23617                 '</div>',
23618         */
23619                 
23620         if (this.href.length > 0) {
23621             img = {
23622                 tag : 'a',
23623                 href: this.href,
23624                 contenteditable : 'true',
23625                 cn : [
23626                     img
23627                 ]
23628             };
23629         }
23630         
23631         
23632         if (this.video_url.length > 0) {
23633             img = {
23634                 tag : 'div',
23635                 cls : this.cls,
23636                 frameborder : 0,
23637                 allowfullscreen : true,
23638                 width : 420,  // these are for video tricks - that we replace the outer
23639                 height : 315,
23640                 src : this.video_url,
23641                 cn : [
23642                     img
23643                 ]
23644             };
23645         }
23646         
23647         return  {
23648             tag: 'figure',
23649             'data-block' : 'Figure',
23650             contenteditable : 'false',
23651             style : {
23652                 display: 'block',
23653                 float :  this.align ,
23654                 'max-width':  this.width,
23655                 width : 'auto',
23656                 margin:  m,
23657                 padding: '10px'
23658                 
23659             },
23660            
23661             
23662             align : this.align,
23663             cn : [
23664                 img,
23665               
23666                 {
23667                     tag: 'figcaption',
23668                     
23669                     style : {
23670                         'text-align': 'left',
23671                         'margin-top' : '16px',
23672                         'font-size' : '16px',
23673                         'line-height' : '24px',
23674                          display : this.caption_display
23675                     },
23676                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
23677                     cn : [
23678                         {
23679                             // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
23680                             tag : 'i',
23681                             contenteditable : true,
23682                             html : this.caption
23683                         }
23684                     ]
23685                     
23686                 }
23687             ]
23688         };
23689          
23690     },
23691     
23692     readElement : function(node)
23693     {
23694         // this should not really come from the link...
23695         this.video_url = this.getVal(node, 'div', 'src');
23696         this.cls = this.getVal(node, 'div', 'class');
23697         this.href = this.getVal(node, 'a', 'href');
23698         
23699         this.image_src = this.getVal(node, 'img', 'src');
23700          
23701         this.align = this.getVal(node, 'figure', 'align');
23702         this.caption = this.getVal(node, 'figcaption', 'html');
23703         // remove '<i>
23704         if (this.caption.trim().match(/^<i[^>]*>/i)) {
23705             this.caption = this.caption.trim().replace(/^<i[^>]*>/i, '').replace(/^<\/i>$/i, '');
23706         }
23707         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
23708         this.width = this.getVal(node, 'figure', 'style', 'max-width');
23709         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
23710         
23711     },
23712     removeNode : function()
23713     {
23714         return this.node;
23715     }
23716     
23717   
23718    
23719      
23720     
23721     
23722     
23723     
23724 })
23725
23726  
23727
23728 /**
23729  * @class Roo.htmleditor.BlockTable
23730  * Block that manages a table
23731  * 
23732  * @constructor
23733  * Create a new Filter.
23734  * @param {Object} config Configuration options
23735  */
23736
23737 Roo.htmleditor.BlockTable = function(cfg)
23738 {
23739     if (cfg.node) {
23740         this.readElement(cfg.node);
23741         this.updateElement(cfg.node);
23742     }
23743     Roo.apply(this, cfg);
23744     if (!cfg.node) {
23745         this.rows = [];
23746         for(var r = 0; r < this.no_row; r++) {
23747             this.rows[r] = [];
23748             for(var c = 0; c < this.no_col; c++) {
23749                 this.rows[r][c] = this.emptyCell();
23750             }
23751         }
23752     }
23753     
23754     
23755 }
23756 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
23757  
23758     rows : false,
23759     no_col : 1,
23760     no_row : 1,
23761     
23762     
23763     width: '100%',
23764     
23765     // used by context menu
23766     friendly_name : 'Table',
23767     deleteTitle : 'Delete Table',
23768     // context menu is drawn once..
23769     
23770     contextMenu : function(toolbar)
23771     {
23772         
23773         var block = function() {
23774             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23775         };
23776         
23777         
23778         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23779         
23780         var syncValue = toolbar.editorcore.syncValue;
23781         
23782         var fields = {};
23783         
23784         return [
23785             {
23786                 xtype : 'TextItem',
23787                 text : "Width: ",
23788                 xns : rooui.Toolbar  //Boostrap?
23789             },
23790             {
23791                 xtype : 'ComboBox',
23792                 allowBlank : false,
23793                 displayField : 'val',
23794                 editable : true,
23795                 listWidth : 100,
23796                 triggerAction : 'all',
23797                 typeAhead : true,
23798                 valueField : 'val',
23799                 width : 100,
23800                 name : 'width',
23801                 listeners : {
23802                     select : function (combo, r, index)
23803                     {
23804                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23805                         var b = block();
23806                         b.width = r.get('val');
23807                         b.updateElement();
23808                         syncValue();
23809                         toolbar.editorcore.onEditorEvent();
23810                     }
23811                 },
23812                 xns : rooui.form,
23813                 store : {
23814                     xtype : 'SimpleStore',
23815                     data : [
23816                         ['100%'],
23817                         ['auto']
23818                     ],
23819                     fields : [ 'val'],
23820                     xns : Roo.data
23821                 }
23822             },
23823             // -------- Cols
23824             
23825             {
23826                 xtype : 'TextItem',
23827                 text : "Columns: ",
23828                 xns : rooui.Toolbar  //Boostrap?
23829             },
23830          
23831             {
23832                 xtype : 'Button',
23833                 text: '-',
23834                 listeners : {
23835                     click : function (_self, e)
23836                     {
23837                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23838                         block().removeColumn();
23839                         syncValue();
23840                         toolbar.editorcore.onEditorEvent();
23841                     }
23842                 },
23843                 xns : rooui.Toolbar
23844             },
23845             {
23846                 xtype : 'Button',
23847                 text: '+',
23848                 listeners : {
23849                     click : function (_self, e)
23850                     {
23851                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23852                         block().addColumn();
23853                         syncValue();
23854                         toolbar.editorcore.onEditorEvent();
23855                     }
23856                 },
23857                 xns : rooui.Toolbar
23858             },
23859             // -------- ROWS
23860             {
23861                 xtype : 'TextItem',
23862                 text : "Rows: ",
23863                 xns : rooui.Toolbar  //Boostrap?
23864             },
23865          
23866             {
23867                 xtype : 'Button',
23868                 text: '-',
23869                 listeners : {
23870                     click : function (_self, e)
23871                     {
23872                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23873                         block().removeRow();
23874                         syncValue();
23875                         toolbar.editorcore.onEditorEvent();
23876                     }
23877                 },
23878                 xns : rooui.Toolbar
23879             },
23880             {
23881                 xtype : 'Button',
23882                 text: '+',
23883                 listeners : {
23884                     click : function (_self, e)
23885                     {
23886                         block().addRow();
23887                         syncValue();
23888                         toolbar.editorcore.onEditorEvent();
23889                     }
23890                 },
23891                 xns : rooui.Toolbar
23892             },
23893             // -------- ROWS
23894             {
23895                 xtype : 'Button',
23896                 text: 'Reset Column Widths',
23897                 listeners : {
23898                     
23899                     click : function (_self, e)
23900                     {
23901                         block().resetWidths();
23902                         syncValue();
23903                         toolbar.editorcore.onEditorEvent();
23904                     }
23905                 },
23906                 xns : rooui.Toolbar
23907             } 
23908             
23909             
23910             
23911         ];
23912         
23913     },
23914     
23915     
23916   /**
23917      * create a DomHelper friendly object - for use with
23918      * Roo.DomHelper.markup / overwrite / etc..
23919      * ?? should it be called with option to hide all editing features?
23920      */
23921     toObject : function()
23922     {
23923         
23924         var ret = {
23925             tag : 'table',
23926             contenteditable : 'false', // this stops cell selection from picking the table.
23927             'data-block' : 'Table',
23928             style : {
23929                 width:  this.width,
23930                 border : 'solid 1px #000', // ??? hard coded?
23931                 'border-collapse' : 'collapse' 
23932             },
23933             cn : [
23934                 { tag : 'tbody' , cn : [] }
23935             ]
23936         };
23937         
23938         // do we have a head = not really 
23939         var ncols = 0;
23940         Roo.each(this.rows, function( row ) {
23941             var tr = {
23942                 tag: 'tr',
23943                 style : {
23944                     margin: '6px',
23945                     border : 'solid 1px #000',
23946                     textAlign : 'left' 
23947                 },
23948                 cn : [ ]
23949             };
23950             
23951             ret.cn[0].cn.push(tr);
23952             // does the row have any properties? ?? height?
23953             var nc = 0;
23954             Roo.each(row, function( cell ) {
23955                 
23956                 var td = {
23957                     tag : 'td',
23958                     contenteditable :  'true',
23959                     'data-block' : 'Td',
23960                     html : cell.html,
23961                     style : cell.style
23962                 };
23963                 if (cell.colspan > 1) {
23964                     td.colspan = cell.colspan ;
23965                     nc += cell.colspan;
23966                 } else {
23967                     nc++;
23968                 }
23969                 if (cell.rowspan > 1) {
23970                     td.rowspan = cell.rowspan ;
23971                 }
23972                 
23973                 
23974                 // widths ?
23975                 tr.cn.push(td);
23976                     
23977                 
23978             }, this);
23979             ncols = Math.max(nc, ncols);
23980             
23981             
23982         }, this);
23983         // add the header row..
23984         
23985         ncols++;
23986          
23987         
23988         return ret;
23989          
23990     },
23991     
23992     readElement : function(node)
23993     {
23994         node  = node ? node : this.node ;
23995         this.width = this.getVal(node, true, 'style', 'width') || '100%';
23996         
23997         this.rows = [];
23998         this.no_row = 0;
23999         var trs = Array.from(node.rows);
24000         trs.forEach(function(tr) {
24001             var row =  [];
24002             this.rows.push(row);
24003             
24004             this.no_row++;
24005             var no_column = 0;
24006             Array.from(tr.cells).forEach(function(td) {
24007                 
24008                 var add = {
24009                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24010                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24011                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24012                     html : td.innerHTML
24013                 };
24014                 no_column += add.colspan;
24015                      
24016                 
24017                 row.push(add);
24018                 
24019                 
24020             },this);
24021             this.no_col = Math.max(this.no_col, no_column);
24022             
24023             
24024         },this);
24025         
24026         
24027     },
24028     normalizeRows: function()
24029     {
24030         var ret= [];
24031         var rid = -1;
24032         this.rows.forEach(function(row) {
24033             rid++;
24034             ret[rid] = [];
24035             row = this.normalizeRow(row);
24036             var cid = 0;
24037             row.forEach(function(c) {
24038                 while (typeof(ret[rid][cid]) != 'undefined') {
24039                     cid++;
24040                 }
24041                 if (typeof(ret[rid]) == 'undefined') {
24042                     ret[rid] = [];
24043                 }
24044                 ret[rid][cid] = c;
24045                 c.row = rid;
24046                 c.col = cid;
24047                 if (c.rowspan < 2) {
24048                     return;
24049                 }
24050                 
24051                 for(var i = 1 ;i < c.rowspan; i++) {
24052                     if (typeof(ret[rid+i]) == 'undefined') {
24053                         ret[rid+i] = [];
24054                     }
24055                     ret[rid+i][cid] = c;
24056                 }
24057             });
24058         }, this);
24059         return ret;
24060     
24061     },
24062     
24063     normalizeRow: function(row)
24064     {
24065         var ret= [];
24066         row.forEach(function(c) {
24067             if (c.colspan < 2) {
24068                 ret.push(c);
24069                 return;
24070             }
24071             for(var i =0 ;i < c.colspan; i++) {
24072                 ret.push(c);
24073             }
24074         });
24075         return ret;
24076     
24077     },
24078     
24079     deleteColumn : function(sel)
24080     {
24081         if (!sel || sel.type != 'col') {
24082             return;
24083         }
24084         if (this.no_col < 2) {
24085             return;
24086         }
24087         
24088         this.rows.forEach(function(row) {
24089             var cols = this.normalizeRow(row);
24090             var col = cols[sel.col];
24091             if (col.colspan > 1) {
24092                 col.colspan --;
24093             } else {
24094                 row.remove(col);
24095             }
24096             
24097         }, this);
24098         this.no_col--;
24099         
24100     },
24101     removeColumn : function()
24102     {
24103         this.deleteColumn({
24104             type: 'col',
24105             col : this.no_col-1
24106         });
24107         this.updateElement();
24108     },
24109     
24110      
24111     addColumn : function()
24112     {
24113         
24114         this.rows.forEach(function(row) {
24115             row.push(this.emptyCell());
24116            
24117         }, this);
24118         this.updateElement();
24119     },
24120     
24121     deleteRow : function(sel)
24122     {
24123         if (!sel || sel.type != 'row') {
24124             return;
24125         }
24126         
24127         if (this.no_row < 2) {
24128             return;
24129         }
24130         
24131         var rows = this.normalizeRows();
24132         
24133         
24134         rows[sel.row].forEach(function(col) {
24135             if (col.rowspan > 1) {
24136                 col.rowspan--;
24137             } else {
24138                 col.remove = 1; // flage it as removed.
24139             }
24140             
24141         }, this);
24142         var newrows = [];
24143         this.rows.forEach(function(row) {
24144             newrow = [];
24145             row.forEach(function(c) {
24146                 if (typeof(c.remove) == 'undefined') {
24147                     newrow.push(c);
24148                 }
24149                 
24150             });
24151             if (newrow.length > 0) {
24152                 newrows.push(row);
24153             }
24154         });
24155         this.rows =  newrows;
24156         
24157         
24158         
24159         this.no_row--;
24160         this.updateElement();
24161         
24162     },
24163     removeRow : function()
24164     {
24165         this.deleteRow({
24166             type: 'row',
24167             row : this.no_row-1
24168         });
24169         
24170     },
24171     
24172      
24173     addRow : function()
24174     {
24175         
24176         var row = [];
24177         for (var i = 0; i < this.no_col; i++ ) {
24178             
24179             row.push(this.emptyCell());
24180            
24181         }
24182         this.rows.push(row);
24183         this.updateElement();
24184         
24185     },
24186      
24187     // the default cell object... at present...
24188     emptyCell : function() {
24189         return (new Roo.htmleditor.BlockTd({})).toObject();
24190         
24191      
24192     },
24193     
24194     removeNode : function()
24195     {
24196         return this.node;
24197     },
24198     
24199     
24200     
24201     resetWidths : function()
24202     {
24203         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24204             var nn = Roo.htmleditor.Block.factory(n);
24205             nn.width = '';
24206             nn.updateElement(n);
24207         });
24208     }
24209     
24210     
24211     
24212     
24213 })
24214
24215 /**
24216  *
24217  * editing a TD?
24218  *
24219  * since selections really work on the table cell, then editing really should work from there
24220  *
24221  * The original plan was to support merging etc... - but that may not be needed yet..
24222  *
24223  * So this simple version will support:
24224  *   add/remove cols
24225  *   adjust the width +/-
24226  *   reset the width...
24227  *   
24228  *
24229  */
24230
24231
24232  
24233
24234 /**
24235  * @class Roo.htmleditor.BlockTable
24236  * Block that manages a table
24237  * 
24238  * @constructor
24239  * Create a new Filter.
24240  * @param {Object} config Configuration options
24241  */
24242
24243 Roo.htmleditor.BlockTd = function(cfg)
24244 {
24245     if (cfg.node) {
24246         this.readElement(cfg.node);
24247         this.updateElement(cfg.node);
24248     }
24249     Roo.apply(this, cfg);
24250      
24251     
24252     
24253 }
24254 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24255  
24256     node : false,
24257     
24258     width: '',
24259     textAlign : 'left',
24260     valign : 'top',
24261     
24262     colspan : 1,
24263     rowspan : 1,
24264     
24265     
24266     // used by context menu
24267     friendly_name : 'Table Cell',
24268     deleteTitle : false, // use our customer delete
24269     
24270     // context menu is drawn once..
24271     
24272     contextMenu : function(toolbar)
24273     {
24274         
24275         var cell = function() {
24276             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24277         };
24278         
24279         var table = function() {
24280             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24281         };
24282         
24283         var lr = false;
24284         var saveSel = function()
24285         {
24286             lr = toolbar.editorcore.getSelection().getRangeAt(0);
24287         }
24288         var restoreSel = function()
24289         {
24290             if (lr) {
24291                 (function() {
24292                     toolbar.editorcore.focus();
24293                     var cr = toolbar.editorcore.getSelection();
24294                     cr.removeAllRanges();
24295                     cr.addRange(lr);
24296                     toolbar.editorcore.onEditorEvent();
24297                 }).defer(10, this);
24298                 
24299                 
24300             }
24301         }
24302         
24303         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24304         
24305         var syncValue = toolbar.editorcore.syncValue;
24306         
24307         var fields = {};
24308         
24309         return [
24310             {
24311                 xtype : 'Button',
24312                 text : 'Edit Table',
24313                 listeners : {
24314                     click : function() {
24315                         var t = toolbar.tb.selectedNode.closest('table');
24316                         toolbar.editorcore.selectNode(t);
24317                         toolbar.editorcore.onEditorEvent();                        
24318                     }
24319                 }
24320                 
24321             },
24322               
24323            
24324              
24325             {
24326                 xtype : 'TextItem',
24327                 text : "Column Width: ",
24328                  xns : rooui.Toolbar 
24329                
24330             },
24331             {
24332                 xtype : 'Button',
24333                 text: '-',
24334                 listeners : {
24335                     click : function (_self, e)
24336                     {
24337                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24338                         cell().shrinkColumn();
24339                         syncValue();
24340                          toolbar.editorcore.onEditorEvent();
24341                     }
24342                 },
24343                 xns : rooui.Toolbar
24344             },
24345             {
24346                 xtype : 'Button',
24347                 text: '+',
24348                 listeners : {
24349                     click : function (_self, e)
24350                     {
24351                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24352                         cell().growColumn();
24353                         syncValue();
24354                         toolbar.editorcore.onEditorEvent();
24355                     }
24356                 },
24357                 xns : rooui.Toolbar
24358             },
24359             
24360             {
24361                 xtype : 'TextItem',
24362                 text : "Vertical Align: ",
24363                 xns : rooui.Toolbar  //Boostrap?
24364             },
24365             {
24366                 xtype : 'ComboBox',
24367                 allowBlank : false,
24368                 displayField : 'val',
24369                 editable : true,
24370                 listWidth : 100,
24371                 triggerAction : 'all',
24372                 typeAhead : true,
24373                 valueField : 'val',
24374                 width : 100,
24375                 name : 'valign',
24376                 listeners : {
24377                     select : function (combo, r, index)
24378                     {
24379                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24380                         var b = cell();
24381                         b.valign = r.get('val');
24382                         b.updateElement();
24383                         syncValue();
24384                         toolbar.editorcore.onEditorEvent();
24385                     }
24386                 },
24387                 xns : rooui.form,
24388                 store : {
24389                     xtype : 'SimpleStore',
24390                     data : [
24391                         ['top'],
24392                         ['middle'],
24393                         ['bottom'] // there are afew more... 
24394                     ],
24395                     fields : [ 'val'],
24396                     xns : Roo.data
24397                 }
24398             },
24399             
24400             {
24401                 xtype : 'TextItem',
24402                 text : "Merge Cells: ",
24403                  xns : rooui.Toolbar 
24404                
24405             },
24406             
24407             
24408             {
24409                 xtype : 'Button',
24410                 text: 'Right',
24411                 listeners : {
24412                     click : function (_self, e)
24413                     {
24414                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24415                         cell().mergeRight();
24416                         //block().growColumn();
24417                         syncValue();
24418                         toolbar.editorcore.onEditorEvent();
24419                     }
24420                 },
24421                 xns : rooui.Toolbar
24422             },
24423              
24424             {
24425                 xtype : 'Button',
24426                 text: 'Below',
24427                 listeners : {
24428                     click : function (_self, e)
24429                     {
24430                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24431                         cell().mergeBelow();
24432                         //block().growColumn();
24433                         syncValue();
24434                         toolbar.editorcore.onEditorEvent();
24435                     }
24436                 },
24437                 xns : rooui.Toolbar
24438             },
24439             {
24440                 xtype : 'TextItem',
24441                 text : "| ",
24442                  xns : rooui.Toolbar 
24443                
24444             },
24445             
24446             {
24447                 xtype : 'Button',
24448                 text: 'Split',
24449                 listeners : {
24450                     click : function (_self, e)
24451                     {
24452                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24453                         cell().split();
24454                         syncValue();
24455                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24456                         toolbar.editorcore.onEditorEvent();
24457                                              
24458                     }
24459                 },
24460                 xns : rooui.Toolbar
24461             },
24462             {
24463                 xtype : 'Fill',
24464                 xns : rooui.Toolbar 
24465                
24466             },
24467         
24468           
24469             {
24470                 xtype : 'Button',
24471                 text: 'Delete',
24472                  
24473                 xns : rooui.Toolbar,
24474                 menu : {
24475                     xtype : 'Menu',
24476                     xns : rooui.menu,
24477                     items : [
24478                         {
24479                             xtype : 'Item',
24480                             html: 'Column',
24481                             listeners : {
24482                                 click : function (_self, e)
24483                                 {
24484                                     var t = table();
24485                                     
24486                                     cell().deleteColumn();
24487                                     syncValue();
24488                                     toolbar.editorcore.selectNode(t.node);
24489                                     toolbar.editorcore.onEditorEvent();   
24490                                 }
24491                             },
24492                             xns : rooui.menu
24493                         },
24494                         {
24495                             xtype : 'Item',
24496                             html: 'Row',
24497                             listeners : {
24498                                 click : function (_self, e)
24499                                 {
24500                                     var t = table();
24501                                     cell().deleteRow();
24502                                     syncValue();
24503                                     
24504                                     toolbar.editorcore.selectNode(t.node);
24505                                     toolbar.editorcore.onEditorEvent();   
24506                                                          
24507                                 }
24508                             },
24509                             xns : rooui.menu
24510                         },
24511                        {
24512                             xtype : 'Separator',
24513                             xns : rooui.menu
24514                         },
24515                         {
24516                             xtype : 'Item',
24517                             html: 'Table',
24518                             listeners : {
24519                                 click : function (_self, e)
24520                                 {
24521                                     var t = table();
24522                                     var nn = t.node.nextSibling || t.node.previousSibling;
24523                                     t.node.parentNode.removeChild(t.node);
24524                                     if (nn) { 
24525                                         toolbar.editorcore.selectNode(nn, true);
24526                                     }
24527                                     toolbar.editorcore.onEditorEvent();   
24528                                                          
24529                                 }
24530                             },
24531                             xns : rooui.menu
24532                         }
24533                     ]
24534                 }
24535             }
24536             
24537             // align... << fixme
24538             
24539         ];
24540         
24541     },
24542     
24543     
24544   /**
24545      * create a DomHelper friendly object - for use with
24546      * Roo.DomHelper.markup / overwrite / etc..
24547      * ?? should it be called with option to hide all editing features?
24548      */
24549  /**
24550      * create a DomHelper friendly object - for use with
24551      * Roo.DomHelper.markup / overwrite / etc..
24552      * ?? should it be called with option to hide all editing features?
24553      */
24554     toObject : function()
24555     {
24556         
24557         var ret = {
24558             tag : 'td',
24559             contenteditable : 'true', // this stops cell selection from picking the table.
24560             'data-block' : 'Td',
24561             valign : this.valign,
24562             style : {  
24563                 'text-align' :  this.textAlign,
24564                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
24565                 'border-collapse' : 'collapse',
24566                 padding : '6px', // 8 for desktop / 4 for mobile
24567                 'vertical-align': this.valign
24568             },
24569             html : this.html
24570         };
24571         if (this.width != '') {
24572             ret.width = this.width;
24573             ret.style.width = this.width;
24574         }
24575         
24576         
24577         if (this.colspan > 1) {
24578             ret.colspan = this.colspan ;
24579         } 
24580         if (this.rowspan > 1) {
24581             ret.rowspan = this.rowspan ;
24582         }
24583         
24584            
24585         
24586         return ret;
24587          
24588     },
24589     
24590     readElement : function(node)
24591     {
24592         node  = node ? node : this.node ;
24593         this.width = node.style.width;
24594         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
24595         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
24596         this.html = node.innerHTML;
24597         
24598         
24599     },
24600      
24601     // the default cell object... at present...
24602     emptyCell : function() {
24603         return {
24604             colspan :  1,
24605             rowspan :  1,
24606             textAlign : 'left',
24607             html : "&nbsp;" // is this going to be editable now?
24608         };
24609      
24610     },
24611     
24612     removeNode : function()
24613     {
24614         return this.node.closest('table');
24615          
24616     },
24617     
24618     cellData : false,
24619     
24620     colWidths : false,
24621     
24622     toTableArray  : function()
24623     {
24624         var ret = [];
24625         var tab = this.node.closest('tr').closest('table');
24626         Array.from(tab.rows).forEach(function(r, ri){
24627             ret[ri] = [];
24628         });
24629         var rn = 0;
24630         this.colWidths = [];
24631         var all_auto = true;
24632         Array.from(tab.rows).forEach(function(r, ri){
24633             
24634             var cn = 0;
24635             Array.from(r.cells).forEach(function(ce, ci){
24636                 var c =  {
24637                     cell : ce,
24638                     row : rn,
24639                     col: cn,
24640                     colspan : ce.colSpan,
24641                     rowspan : ce.rowSpan
24642                 };
24643                 if (ce.isEqualNode(this.node)) {
24644                     this.cellData = c;
24645                 }
24646                 // if we have been filled up by a row?
24647                 if (typeof(ret[rn][cn]) != 'undefined') {
24648                     while(typeof(ret[rn][cn]) != 'undefined') {
24649                         cn++;
24650                     }
24651                     c.col = cn;
24652                 }
24653                 
24654                 if (typeof(this.colWidths[cn]) == 'undefined') {
24655                     this.colWidths[cn] =   ce.style.width;
24656                     if (this.colWidths[cn] != '') {
24657                         all_auto = false;
24658                     }
24659                 }
24660                 
24661                 
24662                 if (c.colspan < 2 && c.rowspan < 2 ) {
24663                     ret[rn][cn] = c;
24664                     cn++;
24665                     return;
24666                 }
24667                 for(var j = 0; j < c.rowspan; j++) {
24668                     if (typeof(ret[rn+j]) == 'undefined') {
24669                         continue; // we have a problem..
24670                     }
24671                     ret[rn+j][cn] = c;
24672                     for(var i = 0; i < c.colspan; i++) {
24673                         ret[rn+j][cn+i] = c;
24674                     }
24675                 }
24676                 
24677                 cn += c.colspan;
24678             }, this);
24679             rn++;
24680         }, this);
24681         
24682         // initalize widths.?
24683         // either all widths or no widths..
24684         if (all_auto) {
24685             this.colWidths[0] = false; // no widths flag.
24686         }
24687         
24688         
24689         return ret;
24690         
24691     },
24692     
24693     
24694     
24695     
24696     mergeRight: function()
24697     {
24698          
24699         // get the contents of the next cell along..
24700         var tr = this.node.closest('tr');
24701         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
24702         if (i >= tr.childNodes.length - 1) {
24703             return; // no cells on right to merge with.
24704         }
24705         var table = this.toTableArray();
24706         
24707         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
24708             return; // nothing right?
24709         }
24710         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
24711         // right cell - must be same rowspan and on the same row.
24712         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
24713             return; // right hand side is not same rowspan.
24714         }
24715         
24716         
24717         
24718         this.node.innerHTML += ' ' + rc.cell.innerHTML;
24719         tr.removeChild(rc.cell);
24720         this.colspan += rc.colspan;
24721         this.node.setAttribute('colspan', this.colspan);
24722
24723     },
24724     
24725     
24726     mergeBelow : function()
24727     {
24728         var table = this.toTableArray();
24729         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
24730             return; // no row below
24731         }
24732         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
24733             return; // nothing right?
24734         }
24735         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
24736         
24737         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
24738             return; // right hand side is not same rowspan.
24739         }
24740         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
24741         rc.cell.parentNode.removeChild(rc.cell);
24742         this.rowspan += rc.rowspan;
24743         this.node.setAttribute('rowspan', this.rowspan);
24744     },
24745     
24746     split: function()
24747     {
24748         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
24749             return;
24750         }
24751         var table = this.toTableArray();
24752         var cd = this.cellData;
24753         this.rowspan = 1;
24754         this.colspan = 1;
24755         
24756         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
24757             
24758             
24759             
24760             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
24761                 if (r == cd.row && c == cd.col) {
24762                     this.node.removeAttribute('rowspan');
24763                     this.node.removeAttribute('colspan');
24764                     continue;
24765                 }
24766                  
24767                 var ntd = this.node.cloneNode(); // which col/row should be 0..
24768                 ntd.removeAttribute('id'); //
24769                 //ntd.style.width  = '';
24770                 ntd.innerHTML = '';
24771                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
24772             }
24773             
24774         }
24775         this.redrawAllCells(table);
24776         
24777          
24778         
24779     },
24780     
24781     
24782     
24783     redrawAllCells: function(table)
24784     {
24785         
24786          
24787         var tab = this.node.closest('tr').closest('table');
24788         var ctr = tab.rows[0].parentNode;
24789         Array.from(tab.rows).forEach(function(r, ri){
24790             
24791             Array.from(r.cells).forEach(function(ce, ci){
24792                 ce.parentNode.removeChild(ce);
24793             });
24794             r.parentNode.removeChild(r);
24795         });
24796         for(var r = 0 ; r < table.length; r++) {
24797             var re = tab.rows[r];
24798             
24799             var re = tab.ownerDocument.createElement('tr');
24800             ctr.appendChild(re);
24801             for(var c = 0 ; c < table[r].length; c++) {
24802                 if (table[r][c].cell === false) {
24803                     continue;
24804                 }
24805                 
24806                 re.appendChild(table[r][c].cell);
24807                  
24808                 table[r][c].cell = false;
24809             }
24810         }
24811         
24812     },
24813     updateWidths : function(table)
24814     {
24815         for(var r = 0 ; r < table.length; r++) {
24816            
24817             for(var c = 0 ; c < table[r].length; c++) {
24818                 if (table[r][c].cell === false) {
24819                     continue;
24820                 }
24821                 
24822                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
24823                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
24824                     el.width = Math.floor(this.colWidths[c])  +'%';
24825                     el.updateElement(el.node);
24826                 }
24827                 table[r][c].cell = false; // done
24828             }
24829         }
24830     },
24831     normalizeWidths : function(table)
24832     {
24833     
24834         if (this.colWidths[0] === false) {
24835             var nw = 100.0 / this.colWidths.length;
24836             this.colWidths.forEach(function(w,i) {
24837                 this.colWidths[i] = nw;
24838             },this);
24839             return;
24840         }
24841     
24842         var t = 0, missing = [];
24843         
24844         this.colWidths.forEach(function(w,i) {
24845             //if you mix % and
24846             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
24847             var add =  this.colWidths[i];
24848             if (add > 0) {
24849                 t+=add;
24850                 return;
24851             }
24852             missing.push(i);
24853             
24854             
24855         },this);
24856         var nc = this.colWidths.length;
24857         if (missing.length) {
24858             var mult = (nc - missing.length) / (1.0 * nc);
24859             var t = mult * t;
24860             var ew = (100 -t) / (1.0 * missing.length);
24861             this.colWidths.forEach(function(w,i) {
24862                 if (w > 0) {
24863                     this.colWidths[i] = w * mult;
24864                     return;
24865                 }
24866                 
24867                 this.colWidths[i] = ew;
24868             }, this);
24869             // have to make up numbers..
24870              
24871         }
24872         // now we should have all the widths..
24873         
24874     
24875     },
24876     
24877     shrinkColumn : function()
24878     {
24879         var table = this.toTableArray();
24880         this.normalizeWidths(table);
24881         var col = this.cellData.col;
24882         var nw = this.colWidths[col] * 0.8;
24883         if (nw < 5) {
24884             return;
24885         }
24886         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24887         this.colWidths.forEach(function(w,i) {
24888             if (i == col) {
24889                  this.colWidths[i] = nw;
24890                 return;
24891             }
24892             this.colWidths[i] += otherAdd
24893         }, this);
24894         this.updateWidths(table);
24895          
24896     },
24897     growColumn : function()
24898     {
24899         var table = this.toTableArray();
24900         this.normalizeWidths(table);
24901         var col = this.cellData.col;
24902         var nw = this.colWidths[col] * 1.2;
24903         if (nw > 90) {
24904             return;
24905         }
24906         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24907         this.colWidths.forEach(function(w,i) {
24908             if (i == col) {
24909                 this.colWidths[i] = nw;
24910                 return;
24911             }
24912             this.colWidths[i] -= otherSub
24913         }, this);
24914         this.updateWidths(table);
24915          
24916     },
24917     deleteRow : function()
24918     {
24919         // delete this rows 'tr'
24920         // if any of the cells in this row have a rowspan > 1 && row!= this row..
24921         // then reduce the rowspan.
24922         var table = this.toTableArray();
24923         // this.cellData.row;
24924         for (var i =0;i< table[this.cellData.row].length ; i++) {
24925             var c = table[this.cellData.row][i];
24926             if (c.row != this.cellData.row) {
24927                 
24928                 c.rowspan--;
24929                 c.cell.setAttribute('rowspan', c.rowspan);
24930                 continue;
24931             }
24932             if (c.rowspan > 1) {
24933                 c.rowspan--;
24934                 c.cell.setAttribute('rowspan', c.rowspan);
24935             }
24936         }
24937         table.splice(this.cellData.row,1);
24938         this.redrawAllCells(table);
24939         
24940     },
24941     deleteColumn : function()
24942     {
24943         var table = this.toTableArray();
24944         
24945         for (var i =0;i< table.length ; i++) {
24946             var c = table[i][this.cellData.col];
24947             if (c.col != this.cellData.col) {
24948                 table[i][this.cellData.col].colspan--;
24949             } else if (c.colspan > 1) {
24950                 c.colspan--;
24951                 c.cell.setAttribute('colspan', c.colspan);
24952             }
24953             table[i].splice(this.cellData.col,1);
24954         }
24955         
24956         this.redrawAllCells(table);
24957     }
24958     
24959     
24960     
24961     
24962 })
24963
24964 //<script type="text/javascript">
24965
24966 /*
24967  * Based  Ext JS Library 1.1.1
24968  * Copyright(c) 2006-2007, Ext JS, LLC.
24969  * LGPL
24970  *
24971  */
24972  
24973 /**
24974  * @class Roo.HtmlEditorCore
24975  * @extends Roo.Component
24976  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24977  *
24978  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24979  */
24980
24981 Roo.HtmlEditorCore = function(config){
24982     
24983     
24984     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24985     
24986     
24987     this.addEvents({
24988         /**
24989          * @event initialize
24990          * Fires when the editor is fully initialized (including the iframe)
24991          * @param {Roo.HtmlEditorCore} this
24992          */
24993         initialize: true,
24994         /**
24995          * @event activate
24996          * Fires when the editor is first receives the focus. Any insertion must wait
24997          * until after this event.
24998          * @param {Roo.HtmlEditorCore} this
24999          */
25000         activate: true,
25001          /**
25002          * @event beforesync
25003          * Fires before the textarea is updated with content from the editor iframe. Return false
25004          * to cancel the sync.
25005          * @param {Roo.HtmlEditorCore} this
25006          * @param {String} html
25007          */
25008         beforesync: true,
25009          /**
25010          * @event beforepush
25011          * Fires before the iframe editor is updated with content from the textarea. Return false
25012          * to cancel the push.
25013          * @param {Roo.HtmlEditorCore} this
25014          * @param {String} html
25015          */
25016         beforepush: true,
25017          /**
25018          * @event sync
25019          * Fires when the textarea is updated with content from the editor iframe.
25020          * @param {Roo.HtmlEditorCore} this
25021          * @param {String} html
25022          */
25023         sync: true,
25024          /**
25025          * @event push
25026          * Fires when the iframe editor is updated with content from the textarea.
25027          * @param {Roo.HtmlEditorCore} this
25028          * @param {String} html
25029          */
25030         push: true,
25031         
25032         /**
25033          * @event editorevent
25034          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25035          * @param {Roo.HtmlEditorCore} this
25036          */
25037         editorevent: true 
25038          
25039         
25040     });
25041     
25042     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25043     
25044     // defaults : white / black...
25045     this.applyBlacklists();
25046     
25047     
25048     
25049 };
25050
25051
25052 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25053
25054
25055      /**
25056      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25057      */
25058     
25059     owner : false,
25060     
25061      /**
25062      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25063      *                        Roo.resizable.
25064      */
25065     resizable : false,
25066      /**
25067      * @cfg {Number} height (in pixels)
25068      */   
25069     height: 300,
25070    /**
25071      * @cfg {Number} width (in pixels)
25072      */   
25073     width: 500,
25074      /**
25075      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25076      *         if you are doing an email editor, this probably needs disabling, it's designed
25077      */
25078     autoClean: true,
25079     
25080     /**
25081      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25082      */
25083     enableBlocks : true,
25084     /**
25085      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25086      * 
25087      */
25088     stylesheets: false,
25089      /**
25090      * @cfg {String} language default en - language of text (usefull for rtl languages)
25091      * 
25092      */
25093     language: 'en',
25094     
25095     /**
25096      * @cfg {boolean} allowComments - default false - allow comments in HTML source
25097      *          - by default they are stripped - if you are editing email you may need this.
25098      */
25099     allowComments: false,
25100     // id of frame..
25101     frameId: false,
25102     
25103     // private properties
25104     validationEvent : false,
25105     deferHeight: true,
25106     initialized : false,
25107     activated : false,
25108     sourceEditMode : false,
25109     onFocus : Roo.emptyFn,
25110     iframePad:3,
25111     hideMode:'offsets',
25112     
25113     clearUp: true,
25114     
25115     // blacklist + whitelisted elements..
25116     black: false,
25117     white: false,
25118      
25119     bodyCls : '',
25120
25121     
25122     undoManager : false,
25123     /**
25124      * Protected method that will not generally be called directly. It
25125      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25126      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25127      */
25128     getDocMarkup : function(){
25129         // body styles..
25130         var st = '';
25131         
25132         // inherit styels from page...?? 
25133         if (this.stylesheets === false) {
25134             
25135             Roo.get(document.head).select('style').each(function(node) {
25136                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25137             });
25138             
25139             Roo.get(document.head).select('link').each(function(node) { 
25140                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25141             });
25142             
25143         } else if (!this.stylesheets.length) {
25144                 // simple..
25145                 st = '<style type="text/css">' +
25146                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25147                    '</style>';
25148         } else {
25149             for (var i in this.stylesheets) {
25150                 if (typeof(this.stylesheets[i]) != 'string') {
25151                     continue;
25152                 }
25153                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25154             }
25155             
25156         }
25157         
25158         st +=  '<style type="text/css">' +
25159             'IMG { cursor: pointer } ' +
25160         '</style>';
25161         
25162         st += '<meta name="google" content="notranslate">';
25163         
25164         var cls = 'notranslate roo-htmleditor-body';
25165         
25166         if(this.bodyCls.length){
25167             cls += ' ' + this.bodyCls;
25168         }
25169         
25170         return '<html  class="notranslate" translate="no"><head>' + st  +
25171             //<style type="text/css">' +
25172             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25173             //'</style>' +
25174             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25175     },
25176
25177     // private
25178     onRender : function(ct, position)
25179     {
25180         var _t = this;
25181         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25182         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25183         
25184         
25185         this.el.dom.style.border = '0 none';
25186         this.el.dom.setAttribute('tabIndex', -1);
25187         this.el.addClass('x-hidden hide');
25188         
25189         
25190         
25191         if(Roo.isIE){ // fix IE 1px bogus margin
25192             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25193         }
25194        
25195         
25196         this.frameId = Roo.id();
25197         
25198          
25199         
25200         var iframe = this.owner.wrap.createChild({
25201             tag: 'iframe',
25202             cls: 'form-control', // bootstrap..
25203             id: this.frameId,
25204             name: this.frameId,
25205             frameBorder : 'no',
25206             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25207         }, this.el
25208         );
25209         
25210         
25211         this.iframe = iframe.dom;
25212
25213         this.assignDocWin();
25214         
25215         this.doc.designMode = 'on';
25216        
25217         this.doc.open();
25218         this.doc.write(this.getDocMarkup());
25219         this.doc.close();
25220
25221         
25222         var task = { // must defer to wait for browser to be ready
25223             run : function(){
25224                 //console.log("run task?" + this.doc.readyState);
25225                 this.assignDocWin();
25226                 if(this.doc.body || this.doc.readyState == 'complete'){
25227                     try {
25228                         this.doc.designMode="on";
25229                         
25230                     } catch (e) {
25231                         return;
25232                     }
25233                     Roo.TaskMgr.stop(task);
25234                     this.initEditor.defer(10, this);
25235                 }
25236             },
25237             interval : 10,
25238             duration: 10000,
25239             scope: this
25240         };
25241         Roo.TaskMgr.start(task);
25242
25243     },
25244
25245     // private
25246     onResize : function(w, h)
25247     {
25248          Roo.log('resize: ' +w + ',' + h );
25249         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25250         if(!this.iframe){
25251             return;
25252         }
25253         if(typeof w == 'number'){
25254             
25255             this.iframe.style.width = w + 'px';
25256         }
25257         if(typeof h == 'number'){
25258             
25259             this.iframe.style.height = h + 'px';
25260             if(this.doc){
25261                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25262             }
25263         }
25264         
25265     },
25266
25267     /**
25268      * Toggles the editor between standard and source edit mode.
25269      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25270      */
25271     toggleSourceEdit : function(sourceEditMode){
25272         
25273         this.sourceEditMode = sourceEditMode === true;
25274         
25275         if(this.sourceEditMode){
25276  
25277             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
25278             
25279         }else{
25280             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25281             //this.iframe.className = '';
25282             this.deferFocus();
25283         }
25284         //this.setSize(this.owner.wrap.getSize());
25285         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25286     },
25287
25288     
25289   
25290
25291     /**
25292      * Protected method that will not generally be called directly. If you need/want
25293      * custom HTML cleanup, this is the method you should override.
25294      * @param {String} html The HTML to be cleaned
25295      * return {String} The cleaned HTML
25296      */
25297     cleanHtml : function(html)
25298     {
25299         html = String(html);
25300         if(html.length > 5){
25301             if(Roo.isSafari){ // strip safari nonsense
25302                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25303             }
25304         }
25305         if(html == '&nbsp;'){
25306             html = '';
25307         }
25308         return html;
25309     },
25310
25311     /**
25312      * HTML Editor -> Textarea
25313      * Protected method that will not generally be called directly. Syncs the contents
25314      * of the editor iframe with the textarea.
25315      */
25316     syncValue : function()
25317     {
25318         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25319         if(this.initialized){
25320             
25321             this.undoManager.addEvent();
25322
25323             
25324             var bd = (this.doc.body || this.doc.documentElement);
25325            
25326             
25327             var sel = this.win.getSelection();
25328             
25329             var div = document.createElement('div');
25330             div.innerHTML = bd.innerHTML;
25331             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25332             if (gtx.length > 0) {
25333                 var rm = gtx.item(0).parentNode;
25334                 rm.parentNode.removeChild(rm);
25335             }
25336             
25337            
25338             if (this.enableBlocks) {
25339                 new Roo.htmleditor.FilterBlock({ node : div });
25340             }
25341             //?? tidy?
25342             var tidy = new Roo.htmleditor.TidySerializer({
25343                 inner:  true
25344             });
25345             var html  = tidy.serialize(div);
25346             
25347             
25348             if(Roo.isSafari){
25349                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25350                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25351                 if(m && m[1]){
25352                     html = '<div style="'+m[0]+'">' + html + '</div>';
25353                 }
25354             }
25355             html = this.cleanHtml(html);
25356             // fix up the special chars.. normaly like back quotes in word...
25357             // however we do not want to do this with chinese..
25358             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25359                 
25360                 var cc = match.charCodeAt();
25361
25362                 // Get the character value, handling surrogate pairs
25363                 if (match.length == 2) {
25364                     // It's a surrogate pair, calculate the Unicode code point
25365                     var high = match.charCodeAt(0) - 0xD800;
25366                     var low  = match.charCodeAt(1) - 0xDC00;
25367                     cc = (high * 0x400) + low + 0x10000;
25368                 }  else if (
25369                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25370                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25371                     (cc >= 0xf900 && cc < 0xfb00 )
25372                 ) {
25373                         return match;
25374                 }  
25375          
25376                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25377                 return "&#" + cc + ";";
25378                 
25379                 
25380             });
25381             
25382             
25383              
25384             if(this.owner.fireEvent('beforesync', this, html) !== false){
25385                 this.el.dom.value = html;
25386                 this.owner.fireEvent('sync', this, html);
25387             }
25388         }
25389     },
25390
25391     /**
25392      * TEXTAREA -> EDITABLE
25393      * Protected method that will not generally be called directly. Pushes the value of the textarea
25394      * into the iframe editor.
25395      */
25396     pushValue : function()
25397     {
25398         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25399         if(this.initialized){
25400             var v = this.el.dom.value.trim();
25401             
25402             
25403             if(this.owner.fireEvent('beforepush', this, v) !== false){
25404                 var d = (this.doc.body || this.doc.documentElement);
25405                 d.innerHTML = v;
25406                  
25407                 this.el.dom.value = d.innerHTML;
25408                 this.owner.fireEvent('push', this, v);
25409             }
25410             if (this.autoClean) {
25411                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25412                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25413             }
25414             
25415             Roo.htmleditor.Block.initAll(this.doc.body);
25416             this.updateLanguage();
25417             
25418             var lc = this.doc.body.lastChild;
25419             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25420                 // add an extra line at the end.
25421                 this.doc.body.appendChild(this.doc.createElement('br'));
25422             }
25423             
25424             
25425         }
25426     },
25427
25428     // private
25429     deferFocus : function(){
25430         this.focus.defer(10, this);
25431     },
25432
25433     // doc'ed in Field
25434     focus : function(){
25435         if(this.win && !this.sourceEditMode){
25436             this.win.focus();
25437         }else{
25438             this.el.focus();
25439         }
25440     },
25441     
25442     assignDocWin: function()
25443     {
25444         var iframe = this.iframe;
25445         
25446          if(Roo.isIE){
25447             this.doc = iframe.contentWindow.document;
25448             this.win = iframe.contentWindow;
25449         } else {
25450 //            if (!Roo.get(this.frameId)) {
25451 //                return;
25452 //            }
25453 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25454 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25455             
25456             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25457                 return;
25458             }
25459             
25460             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25461             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25462         }
25463     },
25464     
25465     // private
25466     initEditor : function(){
25467         //console.log("INIT EDITOR");
25468         this.assignDocWin();
25469         
25470         
25471         
25472         this.doc.designMode="on";
25473         this.doc.open();
25474         this.doc.write(this.getDocMarkup());
25475         this.doc.close();
25476         
25477         var dbody = (this.doc.body || this.doc.documentElement);
25478         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25479         // this copies styles from the containing element into thsi one..
25480         // not sure why we need all of this..
25481         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25482         
25483         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25484         //ss['background-attachment'] = 'fixed'; // w3c
25485         dbody.bgProperties = 'fixed'; // ie
25486         dbody.setAttribute("translate", "no");
25487         
25488         //Roo.DomHelper.applyStyles(dbody, ss);
25489         Roo.EventManager.on(this.doc, {
25490              
25491             'mouseup': this.onEditorEvent,
25492             'dblclick': this.onEditorEvent,
25493             'click': this.onEditorEvent,
25494             'keyup': this.onEditorEvent,
25495             
25496             buffer:100,
25497             scope: this
25498         });
25499         Roo.EventManager.on(this.doc, {
25500             'paste': this.onPasteEvent,
25501             scope : this
25502         });
25503         if(Roo.isGecko){
25504             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25505         }
25506         //??? needed???
25507         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25508             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25509         }
25510         this.initialized = true;
25511
25512         
25513         // initialize special key events - enter
25514         new Roo.htmleditor.KeyEnter({core : this});
25515         
25516          
25517         
25518         this.owner.fireEvent('initialize', this);
25519         this.pushValue();
25520     },
25521     // this is to prevent a href clicks resulting in a redirect?
25522    
25523     onPasteEvent : function(e,v)
25524     {
25525         // I think we better assume paste is going to be a dirty load of rubish from word..
25526         
25527         // even pasting into a 'email version' of this widget will have to clean up that mess.
25528         var cd = (e.browserEvent.clipboardData || window.clipboardData);
25529         
25530         // check what type of paste - if it's an image, then handle it differently.
25531         if (cd.files.length > 0) {
25532             // pasting images?
25533             var urlAPI = (window.createObjectURL && window) || 
25534                 (window.URL && URL.revokeObjectURL && URL) || 
25535                 (window.webkitURL && webkitURL);
25536     
25537             var url = urlAPI.createObjectURL( cd.files[0]);
25538             this.insertAtCursor('<img src=" + url + ">');
25539             return false;
25540         }
25541         
25542         var html = cd.getData('text/html'); // clipboard event
25543         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25544         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25545         Roo.log(images);
25546         //Roo.log(imgs);
25547         // fixme..
25548         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
25549                        .map(function(g) { return g.toDataURL(); });
25550         
25551         
25552         html = this.cleanWordChars(html);
25553         
25554         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25555         
25556         
25557         var sn = this.getParentElement();
25558         // check if d contains a table, and prevent nesting??
25559         //Roo.log(d.getElementsByTagName('table'));
25560         //Roo.log(sn);
25561         //Roo.log(sn.closest('table'));
25562         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25563             e.preventDefault();
25564             this.insertAtCursor("You can not nest tables");
25565             //Roo.log("prevent?"); // fixme - 
25566             return false;
25567         }
25568         
25569         if (images.length > 0) {
25570             Roo.each(d.getElementsByTagName('img'), function(img, i) {
25571                 img.setAttribute('src', images[i]);
25572             });
25573         }
25574         if (this.autoClean) {
25575             new Roo.htmleditor.FilterStyleToTag({ node : d });
25576             new Roo.htmleditor.FilterAttributes({
25577                 node : d,
25578                 attrib_white : ['href', 'src', 'name', 'align'],
25579                 attrib_clean : ['href', 'src' ] 
25580             });
25581             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25582             // should be fonts..
25583             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
25584             new Roo.htmleditor.FilterParagraph({ node : d });
25585             new Roo.htmleditor.FilterSpan({ node : d });
25586             new Roo.htmleditor.FilterLongBr({ node : d });
25587         }
25588         if (this.enableBlocks) {
25589                 
25590             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25591                 if (img.closest('figure')) { // assume!! that it's aready
25592                     return;
25593                 }
25594                 var fig  = new Roo.htmleditor.BlockFigure({
25595                     image_src  : img.src
25596                 });
25597                 fig.updateElement(img); // replace it..
25598                 
25599             });
25600         }
25601         
25602         
25603         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
25604         if (this.enableBlocks) {
25605             Roo.htmleditor.Block.initAll(this.doc.body);
25606         }
25607         
25608         
25609         e.preventDefault();
25610         return false;
25611         // default behaveiour should be our local cleanup paste? (optional?)
25612         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25613         //this.owner.fireEvent('paste', e, v);
25614     },
25615     // private
25616     onDestroy : function(){
25617         
25618         
25619         
25620         if(this.rendered){
25621             
25622             //for (var i =0; i < this.toolbars.length;i++) {
25623             //    // fixme - ask toolbars for heights?
25624             //    this.toolbars[i].onDestroy();
25625            // }
25626             
25627             //this.wrap.dom.innerHTML = '';
25628             //this.wrap.remove();
25629         }
25630     },
25631
25632     // private
25633     onFirstFocus : function(){
25634         
25635         this.assignDocWin();
25636         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25637         
25638         this.activated = true;
25639          
25640     
25641         if(Roo.isGecko){ // prevent silly gecko errors
25642             this.win.focus();
25643             var s = this.win.getSelection();
25644             if(!s.focusNode || s.focusNode.nodeType != 3){
25645                 var r = s.getRangeAt(0);
25646                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25647                 r.collapse(true);
25648                 this.deferFocus();
25649             }
25650             try{
25651                 this.execCmd('useCSS', true);
25652                 this.execCmd('styleWithCSS', false);
25653             }catch(e){}
25654         }
25655         this.owner.fireEvent('activate', this);
25656     },
25657
25658     // private
25659     adjustFont: function(btn){
25660         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25661         //if(Roo.isSafari){ // safari
25662         //    adjust *= 2;
25663        // }
25664         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25665         if(Roo.isSafari){ // safari
25666             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25667             v =  (v < 10) ? 10 : v;
25668             v =  (v > 48) ? 48 : v;
25669             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25670             
25671         }
25672         
25673         
25674         v = Math.max(1, v+adjust);
25675         
25676         this.execCmd('FontSize', v  );
25677     },
25678
25679     onEditorEvent : function(e)
25680     {
25681          
25682         
25683         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25684             return; // we do not handle this.. (undo manager does..)
25685         }
25686         // in theory this detects if the last element is not a br, then we try and do that.
25687         // its so clicking in space at bottom triggers adding a br and moving the cursor.
25688         if (e &&
25689             e.target.nodeName == 'BODY' &&
25690             e.type == "mouseup" &&
25691             this.doc.body.lastChild
25692            ) {
25693             var lc = this.doc.body.lastChild;
25694             // gtx-trans is google translate plugin adding crap.
25695             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25696                 lc = lc.previousSibling;
25697             }
25698             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25699             // if last element is <BR> - then dont do anything.
25700             
25701                 var ns = this.doc.createElement('br');
25702                 this.doc.body.appendChild(ns);
25703                 range = this.doc.createRange();
25704                 range.setStartAfter(ns);
25705                 range.collapse(true);
25706                 var sel = this.win.getSelection();
25707                 sel.removeAllRanges();
25708                 sel.addRange(range);
25709             }
25710         }
25711         
25712         
25713         
25714         this.fireEditorEvent(e);
25715       //  this.updateToolbar();
25716         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25717     },
25718     
25719     fireEditorEvent: function(e)
25720     {
25721         this.owner.fireEvent('editorevent', this, e);
25722     },
25723
25724     insertTag : function(tg)
25725     {
25726         // could be a bit smarter... -> wrap the current selected tRoo..
25727         if (tg.toLowerCase() == 'span' ||
25728             tg.toLowerCase() == 'code' ||
25729             tg.toLowerCase() == 'sup' ||
25730             tg.toLowerCase() == 'sub' 
25731             ) {
25732             
25733             range = this.createRange(this.getSelection());
25734             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25735             wrappingNode.appendChild(range.extractContents());
25736             range.insertNode(wrappingNode);
25737
25738             return;
25739             
25740             
25741             
25742         }
25743         this.execCmd("formatblock",   tg);
25744         this.undoManager.addEvent(); 
25745     },
25746     
25747     insertText : function(txt)
25748     {
25749         
25750         
25751         var range = this.createRange();
25752         range.deleteContents();
25753                //alert(Sender.getAttribute('label'));
25754                
25755         range.insertNode(this.doc.createTextNode(txt));
25756         this.undoManager.addEvent();
25757     } ,
25758     
25759      
25760
25761     /**
25762      * Executes a Midas editor command on the editor document and performs necessary focus and
25763      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25764      * @param {String} cmd The Midas command
25765      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25766      */
25767     relayCmd : function(cmd, value)
25768     {
25769         
25770         switch (cmd) {
25771             case 'justifyleft':
25772             case 'justifyright':
25773             case 'justifycenter':
25774                 // if we are in a cell, then we will adjust the
25775                 var n = this.getParentElement();
25776                 var td = n.closest('td');
25777                 if (td) {
25778                     var bl = Roo.htmleditor.Block.factory(td);
25779                     bl.textAlign = cmd.replace('justify','');
25780                     bl.updateElement();
25781                     this.owner.fireEvent('editorevent', this);
25782                     return;
25783                 }
25784                 this.execCmd('styleWithCSS', true); // 
25785                 break;
25786             case 'bold':
25787             case 'italic':
25788                 // if there is no selection, then we insert, and set the curson inside it..
25789                 this.execCmd('styleWithCSS', false); 
25790                 break;
25791                 
25792         
25793             default:
25794                 break;
25795         }
25796         
25797         
25798         this.win.focus();
25799         this.execCmd(cmd, value);
25800         this.owner.fireEvent('editorevent', this);
25801         //this.updateToolbar();
25802         this.owner.deferFocus();
25803     },
25804
25805     /**
25806      * Executes a Midas editor command directly on the editor document.
25807      * For visual commands, you should use {@link #relayCmd} instead.
25808      * <b>This should only be called after the editor is initialized.</b>
25809      * @param {String} cmd The Midas command
25810      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25811      */
25812     execCmd : function(cmd, value){
25813         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25814         this.syncValue();
25815     },
25816  
25817  
25818    
25819     /**
25820      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25821      * to insert tRoo.
25822      * @param {String} text | dom node.. 
25823      */
25824     insertAtCursor : function(text)
25825     {
25826         
25827         if(!this.activated){
25828             return;
25829         }
25830          
25831         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25832             this.win.focus();
25833             
25834             
25835             // from jquery ui (MIT licenced)
25836             var range, node;
25837             var win = this.win;
25838             
25839             if (win.getSelection && win.getSelection().getRangeAt) {
25840                 
25841                 // delete the existing?
25842                 
25843                 this.createRange(this.getSelection()).deleteContents();
25844                 range = win.getSelection().getRangeAt(0);
25845                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25846                 range.insertNode(node);
25847                 range = range.cloneRange();
25848                 range.collapse(false);
25849                  
25850                 win.getSelection().removeAllRanges();
25851                 win.getSelection().addRange(range);
25852                 
25853                 
25854                 
25855             } else if (win.document.selection && win.document.selection.createRange) {
25856                 // no firefox support
25857                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25858                 win.document.selection.createRange().pasteHTML(txt);
25859             
25860             } else {
25861                 // no firefox support
25862                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25863                 this.execCmd('InsertHTML', txt);
25864             } 
25865             this.syncValue();
25866             
25867             this.deferFocus();
25868         }
25869     },
25870  // private
25871     mozKeyPress : function(e){
25872         if(e.ctrlKey){
25873             var c = e.getCharCode(), cmd;
25874           
25875             if(c > 0){
25876                 c = String.fromCharCode(c).toLowerCase();
25877                 switch(c){
25878                     case 'b':
25879                         cmd = 'bold';
25880                         break;
25881                     case 'i':
25882                         cmd = 'italic';
25883                         break;
25884                     
25885                     case 'u':
25886                         cmd = 'underline';
25887                         break;
25888                     
25889                     //case 'v':
25890                       //  this.cleanUpPaste.defer(100, this);
25891                       //  return;
25892                         
25893                 }
25894                 if(cmd){
25895                     
25896                     this.relayCmd(cmd);
25897                     //this.win.focus();
25898                     //this.execCmd(cmd);
25899                     //this.deferFocus();
25900                     e.preventDefault();
25901                 }
25902                 
25903             }
25904         }
25905     },
25906
25907     // private
25908     fixKeys : function(){ // load time branching for fastest keydown performance
25909         
25910         
25911         if(Roo.isIE){
25912             return function(e){
25913                 var k = e.getKey(), r;
25914                 if(k == e.TAB){
25915                     e.stopEvent();
25916                     r = this.doc.selection.createRange();
25917                     if(r){
25918                         r.collapse(true);
25919                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25920                         this.deferFocus();
25921                     }
25922                     return;
25923                 }
25924                 /// this is handled by Roo.htmleditor.KeyEnter
25925                  /*
25926                 if(k == e.ENTER){
25927                     r = this.doc.selection.createRange();
25928                     if(r){
25929                         var target = r.parentElement();
25930                         if(!target || target.tagName.toLowerCase() != 'li'){
25931                             e.stopEvent();
25932                             r.pasteHTML('<br/>');
25933                             r.collapse(false);
25934                             r.select();
25935                         }
25936                     }
25937                 }
25938                 */
25939                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25940                 //    this.cleanUpPaste.defer(100, this);
25941                 //    return;
25942                 //}
25943                 
25944                 
25945             };
25946         }else if(Roo.isOpera){
25947             return function(e){
25948                 var k = e.getKey();
25949                 if(k == e.TAB){
25950                     e.stopEvent();
25951                     this.win.focus();
25952                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25953                     this.deferFocus();
25954                 }
25955                
25956                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25957                 //    this.cleanUpPaste.defer(100, this);
25958                  //   return;
25959                 //}
25960                 
25961             };
25962         }else if(Roo.isSafari){
25963             return function(e){
25964                 var k = e.getKey();
25965                 
25966                 if(k == e.TAB){
25967                     e.stopEvent();
25968                     this.execCmd('InsertText','\t');
25969                     this.deferFocus();
25970                     return;
25971                 }
25972                  this.mozKeyPress(e);
25973                 
25974                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25975                  //   this.cleanUpPaste.defer(100, this);
25976                  //   return;
25977                // }
25978                 
25979              };
25980         }
25981     }(),
25982     
25983     getAllAncestors: function()
25984     {
25985         var p = this.getSelectedNode();
25986         var a = [];
25987         if (!p) {
25988             a.push(p); // push blank onto stack..
25989             p = this.getParentElement();
25990         }
25991         
25992         
25993         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25994             a.push(p);
25995             p = p.parentNode;
25996         }
25997         a.push(this.doc.body);
25998         return a;
25999     },
26000     lastSel : false,
26001     lastSelNode : false,
26002     
26003     
26004     getSelection : function() 
26005     {
26006         this.assignDocWin();
26007         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26008     },
26009     /**
26010      * Select a dom node
26011      * @param {DomElement} node the node to select
26012      */
26013     selectNode : function(node, collapse)
26014     {
26015         var nodeRange = node.ownerDocument.createRange();
26016         try {
26017             nodeRange.selectNode(node);
26018         } catch (e) {
26019             nodeRange.selectNodeContents(node);
26020         }
26021         if (collapse === true) {
26022             nodeRange.collapse(true);
26023         }
26024         //
26025         var s = this.win.getSelection();
26026         s.removeAllRanges();
26027         s.addRange(nodeRange);
26028     },
26029     
26030     getSelectedNode: function() 
26031     {
26032         // this may only work on Gecko!!!
26033         
26034         // should we cache this!!!!
26035         
26036          
26037          
26038         var range = this.createRange(this.getSelection()).cloneRange();
26039         
26040         if (Roo.isIE) {
26041             var parent = range.parentElement();
26042             while (true) {
26043                 var testRange = range.duplicate();
26044                 testRange.moveToElementText(parent);
26045                 if (testRange.inRange(range)) {
26046                     break;
26047                 }
26048                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26049                     break;
26050                 }
26051                 parent = parent.parentElement;
26052             }
26053             return parent;
26054         }
26055         
26056         // is ancestor a text element.
26057         var ac =  range.commonAncestorContainer;
26058         if (ac.nodeType == 3) {
26059             ac = ac.parentNode;
26060         }
26061         
26062         var ar = ac.childNodes;
26063          
26064         var nodes = [];
26065         var other_nodes = [];
26066         var has_other_nodes = false;
26067         for (var i=0;i<ar.length;i++) {
26068             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26069                 continue;
26070             }
26071             // fullly contained node.
26072             
26073             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26074                 nodes.push(ar[i]);
26075                 continue;
26076             }
26077             
26078             // probably selected..
26079             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26080                 other_nodes.push(ar[i]);
26081                 continue;
26082             }
26083             // outer..
26084             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26085                 continue;
26086             }
26087             
26088             
26089             has_other_nodes = true;
26090         }
26091         if (!nodes.length && other_nodes.length) {
26092             nodes= other_nodes;
26093         }
26094         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26095             return false;
26096         }
26097         
26098         return nodes[0];
26099     },
26100     
26101     
26102     createRange: function(sel)
26103     {
26104         // this has strange effects when using with 
26105         // top toolbar - not sure if it's a great idea.
26106         //this.editor.contentWindow.focus();
26107         if (typeof sel != "undefined") {
26108             try {
26109                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26110             } catch(e) {
26111                 return this.doc.createRange();
26112             }
26113         } else {
26114             return this.doc.createRange();
26115         }
26116     },
26117     getParentElement: function()
26118     {
26119         
26120         this.assignDocWin();
26121         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26122         
26123         var range = this.createRange(sel);
26124          
26125         try {
26126             var p = range.commonAncestorContainer;
26127             while (p.nodeType == 3) { // text node
26128                 p = p.parentNode;
26129             }
26130             return p;
26131         } catch (e) {
26132             return null;
26133         }
26134     
26135     },
26136     /***
26137      *
26138      * Range intersection.. the hard stuff...
26139      *  '-1' = before
26140      *  '0' = hits..
26141      *  '1' = after.
26142      *         [ -- selected range --- ]
26143      *   [fail]                        [fail]
26144      *
26145      *    basically..
26146      *      if end is before start or  hits it. fail.
26147      *      if start is after end or hits it fail.
26148      *
26149      *   if either hits (but other is outside. - then it's not 
26150      *   
26151      *    
26152      **/
26153     
26154     
26155     // @see http://www.thismuchiknow.co.uk/?p=64.
26156     rangeIntersectsNode : function(range, node)
26157     {
26158         var nodeRange = node.ownerDocument.createRange();
26159         try {
26160             nodeRange.selectNode(node);
26161         } catch (e) {
26162             nodeRange.selectNodeContents(node);
26163         }
26164     
26165         var rangeStartRange = range.cloneRange();
26166         rangeStartRange.collapse(true);
26167     
26168         var rangeEndRange = range.cloneRange();
26169         rangeEndRange.collapse(false);
26170     
26171         var nodeStartRange = nodeRange.cloneRange();
26172         nodeStartRange.collapse(true);
26173     
26174         var nodeEndRange = nodeRange.cloneRange();
26175         nodeEndRange.collapse(false);
26176     
26177         return rangeStartRange.compareBoundaryPoints(
26178                  Range.START_TO_START, nodeEndRange) == -1 &&
26179                rangeEndRange.compareBoundaryPoints(
26180                  Range.START_TO_START, nodeStartRange) == 1;
26181         
26182          
26183     },
26184     rangeCompareNode : function(range, node)
26185     {
26186         var nodeRange = node.ownerDocument.createRange();
26187         try {
26188             nodeRange.selectNode(node);
26189         } catch (e) {
26190             nodeRange.selectNodeContents(node);
26191         }
26192         
26193         
26194         range.collapse(true);
26195     
26196         nodeRange.collapse(true);
26197      
26198         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26199         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26200          
26201         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26202         
26203         var nodeIsBefore   =  ss == 1;
26204         var nodeIsAfter    = ee == -1;
26205         
26206         if (nodeIsBefore && nodeIsAfter) {
26207             return 0; // outer
26208         }
26209         if (!nodeIsBefore && nodeIsAfter) {
26210             return 1; //right trailed.
26211         }
26212         
26213         if (nodeIsBefore && !nodeIsAfter) {
26214             return 2;  // left trailed.
26215         }
26216         // fully contined.
26217         return 3;
26218     },
26219  
26220     cleanWordChars : function(input) {// change the chars to hex code
26221         
26222        var swapCodes  = [ 
26223             [    8211, "&#8211;" ], 
26224             [    8212, "&#8212;" ], 
26225             [    8216,  "'" ],  
26226             [    8217, "'" ],  
26227             [    8220, '"' ],  
26228             [    8221, '"' ],  
26229             [    8226, "*" ],  
26230             [    8230, "..." ]
26231         ]; 
26232         var output = input;
26233         Roo.each(swapCodes, function(sw) { 
26234             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26235             
26236             output = output.replace(swapper, sw[1]);
26237         });
26238         
26239         return output;
26240     },
26241     
26242      
26243     
26244         
26245     
26246     cleanUpChild : function (node)
26247     {
26248         
26249         new Roo.htmleditor.FilterComment({node : node});
26250         new Roo.htmleditor.FilterAttributes({
26251                 node : node,
26252                 attrib_black : this.ablack,
26253                 attrib_clean : this.aclean,
26254                 style_white : this.cwhite,
26255                 style_black : this.cblack
26256         });
26257         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26258         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26259          
26260         
26261     },
26262     
26263     /**
26264      * Clean up MS wordisms...
26265      * @deprecated - use filter directly
26266      */
26267     cleanWord : function(node)
26268     {
26269         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26270         
26271     },
26272    
26273     
26274     /**
26275
26276      * @deprecated - use filters
26277      */
26278     cleanTableWidths : function(node)
26279     {
26280         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26281         
26282  
26283     },
26284     
26285      
26286         
26287     applyBlacklists : function()
26288     {
26289         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26290         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26291         
26292         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
26293         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
26294         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
26295         
26296         this.white = [];
26297         this.black = [];
26298         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26299             if (b.indexOf(tag) > -1) {
26300                 return;
26301             }
26302             this.white.push(tag);
26303             
26304         }, this);
26305         
26306         Roo.each(w, function(tag) {
26307             if (b.indexOf(tag) > -1) {
26308                 return;
26309             }
26310             if (this.white.indexOf(tag) > -1) {
26311                 return;
26312             }
26313             this.white.push(tag);
26314             
26315         }, this);
26316         
26317         
26318         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26319             if (w.indexOf(tag) > -1) {
26320                 return;
26321             }
26322             this.black.push(tag);
26323             
26324         }, this);
26325         
26326         Roo.each(b, function(tag) {
26327             if (w.indexOf(tag) > -1) {
26328                 return;
26329             }
26330             if (this.black.indexOf(tag) > -1) {
26331                 return;
26332             }
26333             this.black.push(tag);
26334             
26335         }, this);
26336         
26337         
26338         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26339         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26340         
26341         this.cwhite = [];
26342         this.cblack = [];
26343         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26344             if (b.indexOf(tag) > -1) {
26345                 return;
26346             }
26347             this.cwhite.push(tag);
26348             
26349         }, this);
26350         
26351         Roo.each(w, function(tag) {
26352             if (b.indexOf(tag) > -1) {
26353                 return;
26354             }
26355             if (this.cwhite.indexOf(tag) > -1) {
26356                 return;
26357             }
26358             this.cwhite.push(tag);
26359             
26360         }, this);
26361         
26362         
26363         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26364             if (w.indexOf(tag) > -1) {
26365                 return;
26366             }
26367             this.cblack.push(tag);
26368             
26369         }, this);
26370         
26371         Roo.each(b, function(tag) {
26372             if (w.indexOf(tag) > -1) {
26373                 return;
26374             }
26375             if (this.cblack.indexOf(tag) > -1) {
26376                 return;
26377             }
26378             this.cblack.push(tag);
26379             
26380         }, this);
26381     },
26382     
26383     setStylesheets : function(stylesheets)
26384     {
26385         if(typeof(stylesheets) == 'string'){
26386             Roo.get(this.iframe.contentDocument.head).createChild({
26387                 tag : 'link',
26388                 rel : 'stylesheet',
26389                 type : 'text/css',
26390                 href : stylesheets
26391             });
26392             
26393             return;
26394         }
26395         var _this = this;
26396      
26397         Roo.each(stylesheets, function(s) {
26398             if(!s.length){
26399                 return;
26400             }
26401             
26402             Roo.get(_this.iframe.contentDocument.head).createChild({
26403                 tag : 'link',
26404                 rel : 'stylesheet',
26405                 type : 'text/css',
26406                 href : s
26407             });
26408         });
26409
26410         
26411     },
26412     
26413     
26414     updateLanguage : function()
26415     {
26416         if (!this.iframe || !this.iframe.contentDocument) {
26417             return;
26418         }
26419         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26420     },
26421     
26422     
26423     removeStylesheets : function()
26424     {
26425         var _this = this;
26426         
26427         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26428             s.remove();
26429         });
26430     },
26431     
26432     setStyle : function(style)
26433     {
26434         Roo.get(this.iframe.contentDocument.head).createChild({
26435             tag : 'style',
26436             type : 'text/css',
26437             html : style
26438         });
26439
26440         return;
26441     }
26442     
26443     // hide stuff that is not compatible
26444     /**
26445      * @event blur
26446      * @hide
26447      */
26448     /**
26449      * @event change
26450      * @hide
26451      */
26452     /**
26453      * @event focus
26454      * @hide
26455      */
26456     /**
26457      * @event specialkey
26458      * @hide
26459      */
26460     /**
26461      * @cfg {String} fieldClass @hide
26462      */
26463     /**
26464      * @cfg {String} focusClass @hide
26465      */
26466     /**
26467      * @cfg {String} autoCreate @hide
26468      */
26469     /**
26470      * @cfg {String} inputType @hide
26471      */
26472     /**
26473      * @cfg {String} invalidClass @hide
26474      */
26475     /**
26476      * @cfg {String} invalidText @hide
26477      */
26478     /**
26479      * @cfg {String} msgFx @hide
26480      */
26481     /**
26482      * @cfg {String} validateOnBlur @hide
26483      */
26484 });
26485
26486 Roo.HtmlEditorCore.white = [
26487         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26488         
26489        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
26490        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
26491        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
26492        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
26493        'TABLE',   'UL',         'XMP', 
26494        
26495        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
26496       'THEAD',   'TR', 
26497      
26498       'DIR', 'MENU', 'OL', 'UL', 'DL',
26499        
26500       'EMBED',  'OBJECT'
26501 ];
26502
26503
26504 Roo.HtmlEditorCore.black = [
26505     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26506         'APPLET', // 
26507         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
26508         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
26509         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
26510         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
26511         //'FONT' // CLEAN LATER..
26512         'COLGROUP', 'COL'   // messy tables.
26513         
26514         
26515 ];
26516 Roo.HtmlEditorCore.clean = [ // ?? needed???
26517      'SCRIPT', 'STYLE', 'TITLE', 'XML'
26518 ];
26519 Roo.HtmlEditorCore.tag_remove = [
26520     'FONT', 'TBODY'  
26521 ];
26522 // attributes..
26523
26524 Roo.HtmlEditorCore.ablack = [
26525     'on'
26526 ];
26527     
26528 Roo.HtmlEditorCore.aclean = [ 
26529     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26530 ];
26531
26532 // protocols..
26533 Roo.HtmlEditorCore.pwhite= [
26534         'http',  'https',  'mailto'
26535 ];
26536
26537 // white listed style attributes.
26538 Roo.HtmlEditorCore.cwhite= [
26539       //  'text-align', /// default is to allow most things..
26540       
26541          
26542 //        'font-size'//??
26543 ];
26544
26545 // black listed style attributes.
26546 Roo.HtmlEditorCore.cblack= [
26547       //  'font-size' -- this can be set by the project 
26548 ];
26549
26550
26551
26552
26553     //<script type="text/javascript">
26554
26555 /*
26556  * Ext JS Library 1.1.1
26557  * Copyright(c) 2006-2007, Ext JS, LLC.
26558  * Licence LGPL
26559  * 
26560  */
26561  
26562  
26563 Roo.form.HtmlEditor = function(config){
26564     
26565     
26566     
26567     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26568     
26569     if (!this.toolbars) {
26570         this.toolbars = [];
26571     }
26572     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26573     
26574     
26575 };
26576
26577 /**
26578  * @class Roo.form.HtmlEditor
26579  * @extends Roo.form.Field
26580  * Provides a lightweight HTML Editor component.
26581  *
26582  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26583  * 
26584  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26585  * supported by this editor.</b><br/><br/>
26586  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26587  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26588  */
26589 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26590     /**
26591      * @cfg {Boolean} clearUp
26592      */
26593     clearUp : true,
26594       /**
26595      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26596      */
26597     toolbars : false,
26598    
26599      /**
26600      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26601      *                        Roo.resizable.
26602      */
26603     resizable : false,
26604      /**
26605      * @cfg {Number} height (in pixels)
26606      */   
26607     height: 300,
26608    /**
26609      * @cfg {Number} width (in pixels)
26610      */   
26611     width: 500,
26612     
26613     /**
26614      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
26615      * 
26616      */
26617     stylesheets: false,
26618     
26619     
26620      /**
26621      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26622      * 
26623      */
26624     cblack: false,
26625     /**
26626      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26627      * 
26628      */
26629     cwhite: false,
26630     
26631      /**
26632      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26633      * 
26634      */
26635     black: false,
26636     /**
26637      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26638      * 
26639      */
26640     white: false,
26641     /**
26642      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26643      */
26644     allowComments: false,
26645     /**
26646      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26647      */
26648     enableBlocks : true,
26649     
26650     /**
26651      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26652      *         if you are doing an email editor, this probably needs disabling, it's designed
26653      */
26654     autoClean: true,
26655     /**
26656      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
26657      */
26658     bodyCls : '',
26659     /**
26660      * @cfg {String} language default en - language of text (usefull for rtl languages)
26661      * 
26662      */
26663     language: 'en',
26664     
26665      
26666     // id of frame..
26667     frameId: false,
26668     
26669     // private properties
26670     validationEvent : false,
26671     deferHeight: true,
26672     initialized : false,
26673     activated : false,
26674     
26675     onFocus : Roo.emptyFn,
26676     iframePad:3,
26677     hideMode:'offsets',
26678     
26679     actionMode : 'container', // defaults to hiding it...
26680     
26681     defaultAutoCreate : { // modified by initCompnoent..
26682         tag: "textarea",
26683         style:"width:500px;height:300px;",
26684         autocomplete: "new-password"
26685     },
26686
26687     // private
26688     initComponent : function(){
26689         this.addEvents({
26690             /**
26691              * @event initialize
26692              * Fires when the editor is fully initialized (including the iframe)
26693              * @param {HtmlEditor} this
26694              */
26695             initialize: true,
26696             /**
26697              * @event activate
26698              * Fires when the editor is first receives the focus. Any insertion must wait
26699              * until after this event.
26700              * @param {HtmlEditor} this
26701              */
26702             activate: true,
26703              /**
26704              * @event beforesync
26705              * Fires before the textarea is updated with content from the editor iframe. Return false
26706              * to cancel the sync.
26707              * @param {HtmlEditor} this
26708              * @param {String} html
26709              */
26710             beforesync: true,
26711              /**
26712              * @event beforepush
26713              * Fires before the iframe editor is updated with content from the textarea. Return false
26714              * to cancel the push.
26715              * @param {HtmlEditor} this
26716              * @param {String} html
26717              */
26718             beforepush: true,
26719              /**
26720              * @event sync
26721              * Fires when the textarea is updated with content from the editor iframe.
26722              * @param {HtmlEditor} this
26723              * @param {String} html
26724              */
26725             sync: true,
26726              /**
26727              * @event push
26728              * Fires when the iframe editor is updated with content from the textarea.
26729              * @param {HtmlEditor} this
26730              * @param {String} html
26731              */
26732             push: true,
26733              /**
26734              * @event editmodechange
26735              * Fires when the editor switches edit modes
26736              * @param {HtmlEditor} this
26737              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26738              */
26739             editmodechange: true,
26740             /**
26741              * @event editorevent
26742              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26743              * @param {HtmlEditor} this
26744              */
26745             editorevent: true,
26746             /**
26747              * @event firstfocus
26748              * Fires when on first focus - needed by toolbars..
26749              * @param {HtmlEditor} this
26750              */
26751             firstfocus: true,
26752             /**
26753              * @event autosave
26754              * Auto save the htmlEditor value as a file into Events
26755              * @param {HtmlEditor} this
26756              */
26757             autosave: true,
26758             /**
26759              * @event savedpreview
26760              * preview the saved version of htmlEditor
26761              * @param {HtmlEditor} this
26762              */
26763             savedpreview: true,
26764             
26765             /**
26766             * @event stylesheetsclick
26767             * Fires when press the Sytlesheets button
26768             * @param {Roo.HtmlEditorCore} this
26769             */
26770             stylesheetsclick: true,
26771             /**
26772             * @event paste
26773             * Fires when press user pastes into the editor
26774             * @param {Roo.HtmlEditorCore} this
26775             */
26776             paste: true 
26777         });
26778         this.defaultAutoCreate =  {
26779             tag: "textarea",
26780             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26781             autocomplete: "new-password"
26782         };
26783     },
26784
26785     /**
26786      * Protected method that will not generally be called directly. It
26787      * is called when the editor creates its toolbar. Override this method if you need to
26788      * add custom toolbar buttons.
26789      * @param {HtmlEditor} editor
26790      */
26791     createToolbar : function(editor){
26792         Roo.log("create toolbars");
26793         if (!editor.toolbars || !editor.toolbars.length) {
26794             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26795         }
26796         
26797         for (var i =0 ; i < editor.toolbars.length;i++) {
26798             editor.toolbars[i] = Roo.factory(
26799                     typeof(editor.toolbars[i]) == 'string' ?
26800                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26801                 Roo.form.HtmlEditor);
26802             editor.toolbars[i].init(editor);
26803         }
26804          
26805         
26806     },
26807     /**
26808      * get the Context selected node
26809      * @returns {DomElement|boolean} selected node if active or false if none
26810      * 
26811      */
26812     getSelectedNode : function()
26813     {
26814         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
26815             return false;
26816         }
26817         return this.toolbars[1].tb.selectedNode;
26818     
26819     },
26820     // private
26821     onRender : function(ct, position)
26822     {
26823         var _t = this;
26824         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26825         
26826         this.wrap = this.el.wrap({
26827             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26828         });
26829         
26830         this.editorcore.onRender(ct, position);
26831          
26832         if (this.resizable) {
26833             this.resizeEl = new Roo.Resizable(this.wrap, {
26834                 pinned : true,
26835                 wrap: true,
26836                 dynamic : true,
26837                 minHeight : this.height,
26838                 height: this.height,
26839                 handles : this.resizable,
26840                 width: this.width,
26841                 listeners : {
26842                     resize : function(r, w, h) {
26843                         _t.onResize(w,h); // -something
26844                     }
26845                 }
26846             });
26847             
26848         }
26849         this.createToolbar(this);
26850        
26851         
26852         if(!this.width){
26853             this.setSize(this.wrap.getSize());
26854         }
26855         if (this.resizeEl) {
26856             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26857             // should trigger onReize..
26858         }
26859         
26860         this.keyNav = new Roo.KeyNav(this.el, {
26861             
26862             "tab" : function(e){
26863                 e.preventDefault();
26864                 
26865                 var value = this.getValue();
26866                 
26867                 var start = this.el.dom.selectionStart;
26868                 var end = this.el.dom.selectionEnd;
26869                 
26870                 if(!e.shiftKey){
26871                     
26872                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26873                     this.el.dom.setSelectionRange(end + 1, end + 1);
26874                     return;
26875                 }
26876                 
26877                 var f = value.substring(0, start).split("\t");
26878                 
26879                 if(f.pop().length != 0){
26880                     return;
26881                 }
26882                 
26883                 this.setValue(f.join("\t") + value.substring(end));
26884                 this.el.dom.setSelectionRange(start - 1, start - 1);
26885                 
26886             },
26887             
26888             "home" : function(e){
26889                 e.preventDefault();
26890                 
26891                 var curr = this.el.dom.selectionStart;
26892                 var lines = this.getValue().split("\n");
26893                 
26894                 if(!lines.length){
26895                     return;
26896                 }
26897                 
26898                 if(e.ctrlKey){
26899                     this.el.dom.setSelectionRange(0, 0);
26900                     return;
26901                 }
26902                 
26903                 var pos = 0;
26904                 
26905                 for (var i = 0; i < lines.length;i++) {
26906                     pos += lines[i].length;
26907                     
26908                     if(i != 0){
26909                         pos += 1;
26910                     }
26911                     
26912                     if(pos < curr){
26913                         continue;
26914                     }
26915                     
26916                     pos -= lines[i].length;
26917                     
26918                     break;
26919                 }
26920                 
26921                 if(!e.shiftKey){
26922                     this.el.dom.setSelectionRange(pos, pos);
26923                     return;
26924                 }
26925                 
26926                 this.el.dom.selectionStart = pos;
26927                 this.el.dom.selectionEnd = curr;
26928             },
26929             
26930             "end" : function(e){
26931                 e.preventDefault();
26932                 
26933                 var curr = this.el.dom.selectionStart;
26934                 var lines = this.getValue().split("\n");
26935                 
26936                 if(!lines.length){
26937                     return;
26938                 }
26939                 
26940                 if(e.ctrlKey){
26941                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26942                     return;
26943                 }
26944                 
26945                 var pos = 0;
26946                 
26947                 for (var i = 0; i < lines.length;i++) {
26948                     
26949                     pos += lines[i].length;
26950                     
26951                     if(i != 0){
26952                         pos += 1;
26953                     }
26954                     
26955                     if(pos < curr){
26956                         continue;
26957                     }
26958                     
26959                     break;
26960                 }
26961                 
26962                 if(!e.shiftKey){
26963                     this.el.dom.setSelectionRange(pos, pos);
26964                     return;
26965                 }
26966                 
26967                 this.el.dom.selectionStart = curr;
26968                 this.el.dom.selectionEnd = pos;
26969             },
26970
26971             scope : this,
26972
26973             doRelay : function(foo, bar, hname){
26974                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26975             },
26976
26977             forceKeyDown: true
26978         });
26979         
26980 //        if(this.autosave && this.w){
26981 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26982 //        }
26983     },
26984
26985     // private
26986     onResize : function(w, h)
26987     {
26988         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26989         var ew = false;
26990         var eh = false;
26991         
26992         if(this.el ){
26993             if(typeof w == 'number'){
26994                 var aw = w - this.wrap.getFrameWidth('lr');
26995                 this.el.setWidth(this.adjustWidth('textarea', aw));
26996                 ew = aw;
26997             }
26998             if(typeof h == 'number'){
26999                 var tbh = 0;
27000                 for (var i =0; i < this.toolbars.length;i++) {
27001                     // fixme - ask toolbars for heights?
27002                     tbh += this.toolbars[i].tb.el.getHeight();
27003                     if (this.toolbars[i].footer) {
27004                         tbh += this.toolbars[i].footer.el.getHeight();
27005                     }
27006                 }
27007                 
27008                 
27009                 
27010                 
27011                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27012                 ah -= 5; // knock a few pixes off for look..
27013 //                Roo.log(ah);
27014                 this.el.setHeight(this.adjustWidth('textarea', ah));
27015                 var eh = ah;
27016             }
27017         }
27018         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27019         this.editorcore.onResize(ew,eh);
27020         
27021     },
27022
27023     /**
27024      * Toggles the editor between standard and source edit mode.
27025      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27026      */
27027     toggleSourceEdit : function(sourceEditMode)
27028     {
27029         this.editorcore.toggleSourceEdit(sourceEditMode);
27030         
27031         if(this.editorcore.sourceEditMode){
27032             Roo.log('editor - showing textarea');
27033             
27034 //            Roo.log('in');
27035 //            Roo.log(this.syncValue());
27036             this.editorcore.syncValue();
27037             this.el.removeClass('x-hidden');
27038             this.el.dom.removeAttribute('tabIndex');
27039             this.el.focus();
27040             this.el.dom.scrollTop = 0;
27041             
27042             
27043             for (var i = 0; i < this.toolbars.length; i++) {
27044                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27045                     this.toolbars[i].tb.hide();
27046                     this.toolbars[i].footer.hide();
27047                 }
27048             }
27049             
27050         }else{
27051             Roo.log('editor - hiding textarea');
27052 //            Roo.log('out')
27053 //            Roo.log(this.pushValue()); 
27054             this.editorcore.pushValue();
27055             
27056             this.el.addClass('x-hidden');
27057             this.el.dom.setAttribute('tabIndex', -1);
27058             
27059             for (var i = 0; i < this.toolbars.length; i++) {
27060                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27061                     this.toolbars[i].tb.show();
27062                     this.toolbars[i].footer.show();
27063                 }
27064             }
27065             
27066             //this.deferFocus();
27067         }
27068         
27069         this.setSize(this.wrap.getSize());
27070         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27071         
27072         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27073     },
27074  
27075     // private (for BoxComponent)
27076     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27077
27078     // private (for BoxComponent)
27079     getResizeEl : function(){
27080         return this.wrap;
27081     },
27082
27083     // private (for BoxComponent)
27084     getPositionEl : function(){
27085         return this.wrap;
27086     },
27087
27088     // private
27089     initEvents : function(){
27090         this.originalValue = this.getValue();
27091     },
27092
27093     /**
27094      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27095      * @method
27096      */
27097     markInvalid : Roo.emptyFn,
27098     /**
27099      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27100      * @method
27101      */
27102     clearInvalid : Roo.emptyFn,
27103
27104     setValue : function(v){
27105         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27106         this.editorcore.pushValue();
27107     },
27108
27109     /**
27110      * update the language in the body - really done by core
27111      * @param {String} language - eg. en / ar / zh-CN etc..
27112      */
27113     updateLanguage : function(lang)
27114     {
27115         this.language = lang;
27116         this.editorcore.language = lang;
27117         this.editorcore.updateLanguage();
27118      
27119     },
27120     // private
27121     deferFocus : function(){
27122         this.focus.defer(10, this);
27123     },
27124
27125     // doc'ed in Field
27126     focus : function(){
27127         this.editorcore.focus();
27128         
27129     },
27130       
27131
27132     // private
27133     onDestroy : function(){
27134         
27135         
27136         
27137         if(this.rendered){
27138             
27139             for (var i =0; i < this.toolbars.length;i++) {
27140                 // fixme - ask toolbars for heights?
27141                 this.toolbars[i].onDestroy();
27142             }
27143             
27144             this.wrap.dom.innerHTML = '';
27145             this.wrap.remove();
27146         }
27147     },
27148
27149     // private
27150     onFirstFocus : function(){
27151         //Roo.log("onFirstFocus");
27152         this.editorcore.onFirstFocus();
27153          for (var i =0; i < this.toolbars.length;i++) {
27154             this.toolbars[i].onFirstFocus();
27155         }
27156         
27157     },
27158     
27159     // private
27160     syncValue : function()
27161     {
27162         this.editorcore.syncValue();
27163     },
27164     
27165     pushValue : function()
27166     {
27167         this.editorcore.pushValue();
27168     },
27169     
27170     setStylesheets : function(stylesheets)
27171     {
27172         this.editorcore.setStylesheets(stylesheets);
27173     },
27174     
27175     removeStylesheets : function()
27176     {
27177         this.editorcore.removeStylesheets();
27178     }
27179      
27180     
27181     // hide stuff that is not compatible
27182     /**
27183      * @event blur
27184      * @hide
27185      */
27186     /**
27187      * @event change
27188      * @hide
27189      */
27190     /**
27191      * @event focus
27192      * @hide
27193      */
27194     /**
27195      * @event specialkey
27196      * @hide
27197      */
27198     /**
27199      * @cfg {String} fieldClass @hide
27200      */
27201     /**
27202      * @cfg {String} focusClass @hide
27203      */
27204     /**
27205      * @cfg {String} autoCreate @hide
27206      */
27207     /**
27208      * @cfg {String} inputType @hide
27209      */
27210     /**
27211      * @cfg {String} invalidClass @hide
27212      */
27213     /**
27214      * @cfg {String} invalidText @hide
27215      */
27216     /**
27217      * @cfg {String} msgFx @hide
27218      */
27219     /**
27220      * @cfg {String} validateOnBlur @hide
27221      */
27222 });
27223  
27224     /*
27225  * Based on
27226  * Ext JS Library 1.1.1
27227  * Copyright(c) 2006-2007, Ext JS, LLC.
27228  *  
27229  
27230  */
27231
27232 /**
27233  * @class Roo.form.HtmlEditor.ToolbarStandard
27234  * Basic Toolbar
27235
27236  * Usage:
27237  *
27238  new Roo.form.HtmlEditor({
27239     ....
27240     toolbars : [
27241         new Roo.form.HtmlEditorToolbar1({
27242             disable : { fonts: 1 , format: 1, ..., ... , ...],
27243             btns : [ .... ]
27244         })
27245     }
27246      
27247  * 
27248  * @cfg {Object} disable List of elements to disable..
27249  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27250  * 
27251  * 
27252  * NEEDS Extra CSS? 
27253  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27254  */
27255  
27256 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27257 {
27258     
27259     Roo.apply(this, config);
27260     
27261     // default disabled, based on 'good practice'..
27262     this.disable = this.disable || {};
27263     Roo.applyIf(this.disable, {
27264         fontSize : true,
27265         colors : true,
27266         specialElements : true
27267     });
27268     
27269     
27270     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27271     // dont call parent... till later.
27272 }
27273
27274 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27275     
27276     tb: false,
27277     
27278     rendered: false,
27279     
27280     editor : false,
27281     editorcore : false,
27282     /**
27283      * @cfg {Object} disable  List of toolbar elements to disable
27284          
27285      */
27286     disable : false,
27287     
27288     
27289      /**
27290      * @cfg {String} createLinkText The default text for the create link prompt
27291      */
27292     createLinkText : 'Please enter the URL for the link:',
27293     /**
27294      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27295      */
27296     defaultLinkValue : 'http:/'+'/',
27297    
27298     
27299       /**
27300      * @cfg {Array} fontFamilies An array of available font families
27301      */
27302     fontFamilies : [
27303         'Arial',
27304         'Courier New',
27305         'Tahoma',
27306         'Times New Roman',
27307         'Verdana'
27308     ],
27309     
27310     specialChars : [
27311            "&#169;",
27312           "&#174;",     
27313           "&#8482;",    
27314           "&#163;" ,    
27315          // "&#8212;",    
27316           "&#8230;",    
27317           "&#247;" ,    
27318         //  "&#225;" ,     ?? a acute?
27319            "&#8364;"    , //Euro
27320        //   "&#8220;"    ,
27321         //  "&#8221;"    ,
27322         //  "&#8226;"    ,
27323           "&#176;"  //   , // degrees
27324
27325          // "&#233;"     , // e ecute
27326          // "&#250;"     , // u ecute?
27327     ],
27328     
27329     specialElements : [
27330         {
27331             text: "Insert Table",
27332             xtype: 'MenuItem',
27333             xns : Roo.Menu,
27334             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27335                 
27336         },
27337         {    
27338             text: "Insert Image",
27339             xtype: 'MenuItem',
27340             xns : Roo.Menu,
27341             ihtml : '<img src="about:blank"/>'
27342             
27343         }
27344         
27345          
27346     ],
27347     
27348     
27349     inputElements : [ 
27350             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27351             "input:submit", "input:button", "select", "textarea", "label" ],
27352     formats : [
27353         ["p"] ,  
27354         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27355         ["pre"],[ "code"], 
27356         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27357         ['div'],['span'],
27358         ['sup'],['sub']
27359     ],
27360     
27361     cleanStyles : [
27362         "font-size"
27363     ],
27364      /**
27365      * @cfg {String} defaultFont default font to use.
27366      */
27367     defaultFont: 'tahoma',
27368    
27369     fontSelect : false,
27370     
27371     
27372     formatCombo : false,
27373     
27374     init : function(editor)
27375     {
27376         this.editor = editor;
27377         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27378         var editorcore = this.editorcore;
27379         
27380         var _t = this;
27381         
27382         var fid = editorcore.frameId;
27383         var etb = this;
27384         function btn(id, toggle, handler){
27385             var xid = fid + '-'+ id ;
27386             return {
27387                 id : xid,
27388                 cmd : id,
27389                 cls : 'x-btn-icon x-edit-'+id,
27390                 enableToggle:toggle !== false,
27391                 scope: _t, // was editor...
27392                 handler:handler||_t.relayBtnCmd,
27393                 clickEvent:'mousedown',
27394                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27395                 tabIndex:-1
27396             };
27397         }
27398         
27399         
27400         
27401         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27402         this.tb = tb;
27403          // stop form submits
27404         tb.el.on('click', function(e){
27405             e.preventDefault(); // what does this do?
27406         });
27407
27408         if(!this.disable.font) { // && !Roo.isSafari){
27409             /* why no safari for fonts 
27410             editor.fontSelect = tb.el.createChild({
27411                 tag:'select',
27412                 tabIndex: -1,
27413                 cls:'x-font-select',
27414                 html: this.createFontOptions()
27415             });
27416             
27417             editor.fontSelect.on('change', function(){
27418                 var font = editor.fontSelect.dom.value;
27419                 editor.relayCmd('fontname', font);
27420                 editor.deferFocus();
27421             }, editor);
27422             
27423             tb.add(
27424                 editor.fontSelect.dom,
27425                 '-'
27426             );
27427             */
27428             
27429         };
27430         if(!this.disable.formats){
27431             this.formatCombo = new Roo.form.ComboBox({
27432                 store: new Roo.data.SimpleStore({
27433                     id : 'tag',
27434                     fields: ['tag'],
27435                     data : this.formats // from states.js
27436                 }),
27437                 blockFocus : true,
27438                 name : '',
27439                 //autoCreate : {tag: "div",  size: "20"},
27440                 displayField:'tag',
27441                 typeAhead: false,
27442                 mode: 'local',
27443                 editable : false,
27444                 triggerAction: 'all',
27445                 emptyText:'Add tag',
27446                 selectOnFocus:true,
27447                 width:135,
27448                 listeners : {
27449                     'select': function(c, r, i) {
27450                         editorcore.insertTag(r.get('tag'));
27451                         editor.focus();
27452                     }
27453                 }
27454
27455             });
27456             tb.addField(this.formatCombo);
27457             
27458         }
27459         
27460         if(!this.disable.format){
27461             tb.add(
27462                 btn('bold'),
27463                 btn('italic'),
27464                 btn('underline'),
27465                 btn('strikethrough')
27466             );
27467         };
27468         if(!this.disable.fontSize){
27469             tb.add(
27470                 '-',
27471                 
27472                 
27473                 btn('increasefontsize', false, editorcore.adjustFont),
27474                 btn('decreasefontsize', false, editorcore.adjustFont)
27475             );
27476         };
27477         
27478         
27479         if(!this.disable.colors){
27480             tb.add(
27481                 '-', {
27482                     id:editorcore.frameId +'-forecolor',
27483                     cls:'x-btn-icon x-edit-forecolor',
27484                     clickEvent:'mousedown',
27485                     tooltip: this.buttonTips['forecolor'] || undefined,
27486                     tabIndex:-1,
27487                     menu : new Roo.menu.ColorMenu({
27488                         allowReselect: true,
27489                         focus: Roo.emptyFn,
27490                         value:'000000',
27491                         plain:true,
27492                         selectHandler: function(cp, color){
27493                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27494                             editor.deferFocus();
27495                         },
27496                         scope: editorcore,
27497                         clickEvent:'mousedown'
27498                     })
27499                 }, {
27500                     id:editorcore.frameId +'backcolor',
27501                     cls:'x-btn-icon x-edit-backcolor',
27502                     clickEvent:'mousedown',
27503                     tooltip: this.buttonTips['backcolor'] || undefined,
27504                     tabIndex:-1,
27505                     menu : new Roo.menu.ColorMenu({
27506                         focus: Roo.emptyFn,
27507                         value:'FFFFFF',
27508                         plain:true,
27509                         allowReselect: true,
27510                         selectHandler: function(cp, color){
27511                             if(Roo.isGecko){
27512                                 editorcore.execCmd('useCSS', false);
27513                                 editorcore.execCmd('hilitecolor', color);
27514                                 editorcore.execCmd('useCSS', true);
27515                                 editor.deferFocus();
27516                             }else{
27517                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27518                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27519                                 editor.deferFocus();
27520                             }
27521                         },
27522                         scope:editorcore,
27523                         clickEvent:'mousedown'
27524                     })
27525                 }
27526             );
27527         };
27528         // now add all the items...
27529         
27530
27531         if(!this.disable.alignments){
27532             tb.add(
27533                 '-',
27534                 btn('justifyleft'),
27535                 btn('justifycenter'),
27536                 btn('justifyright')
27537             );
27538         };
27539
27540         //if(!Roo.isSafari){
27541             if(!this.disable.links){
27542                 tb.add(
27543                     '-',
27544                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27545                 );
27546             };
27547
27548             if(!this.disable.lists){
27549                 tb.add(
27550                     '-',
27551                     btn('insertorderedlist'),
27552                     btn('insertunorderedlist')
27553                 );
27554             }
27555             if(!this.disable.sourceEdit){
27556                 tb.add(
27557                     '-',
27558                     btn('sourceedit', true, function(btn){
27559                         this.toggleSourceEdit(btn.pressed);
27560                     })
27561                 );
27562             }
27563         //}
27564         
27565         var smenu = { };
27566         // special menu.. - needs to be tidied up..
27567         if (!this.disable.special) {
27568             smenu = {
27569                 text: "&#169;",
27570                 cls: 'x-edit-none',
27571                 
27572                 menu : {
27573                     items : []
27574                 }
27575             };
27576             for (var i =0; i < this.specialChars.length; i++) {
27577                 smenu.menu.items.push({
27578                     
27579                     html: this.specialChars[i],
27580                     handler: function(a,b) {
27581                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27582                         //editor.insertAtCursor(a.html);
27583                         
27584                     },
27585                     tabIndex:-1
27586                 });
27587             }
27588             
27589             
27590             tb.add(smenu);
27591             
27592             
27593         }
27594         
27595         var cmenu = { };
27596         if (!this.disable.cleanStyles) {
27597             cmenu = {
27598                 cls: 'x-btn-icon x-btn-clear',
27599                 
27600                 menu : {
27601                     items : []
27602                 }
27603             };
27604             for (var i =0; i < this.cleanStyles.length; i++) {
27605                 cmenu.menu.items.push({
27606                     actiontype : this.cleanStyles[i],
27607                     html: 'Remove ' + this.cleanStyles[i],
27608                     handler: function(a,b) {
27609 //                        Roo.log(a);
27610 //                        Roo.log(b);
27611                         var c = Roo.get(editorcore.doc.body);
27612                         c.select('[style]').each(function(s) {
27613                             s.dom.style.removeProperty(a.actiontype);
27614                         });
27615                         editorcore.syncValue();
27616                     },
27617                     tabIndex:-1
27618                 });
27619             }
27620             cmenu.menu.items.push({
27621                 actiontype : 'tablewidths',
27622                 html: 'Remove Table Widths',
27623                 handler: function(a,b) {
27624                     editorcore.cleanTableWidths();
27625                     editorcore.syncValue();
27626                 },
27627                 tabIndex:-1
27628             });
27629             cmenu.menu.items.push({
27630                 actiontype : 'word',
27631                 html: 'Remove MS Word Formating',
27632                 handler: function(a,b) {
27633                     editorcore.cleanWord();
27634                     editorcore.syncValue();
27635                 },
27636                 tabIndex:-1
27637             });
27638             
27639             cmenu.menu.items.push({
27640                 actiontype : 'all',
27641                 html: 'Remove All Styles',
27642                 handler: function(a,b) {
27643                     
27644                     var c = Roo.get(editorcore.doc.body);
27645                     c.select('[style]').each(function(s) {
27646                         s.dom.removeAttribute('style');
27647                     });
27648                     editorcore.syncValue();
27649                 },
27650                 tabIndex:-1
27651             });
27652             
27653             cmenu.menu.items.push({
27654                 actiontype : 'all',
27655                 html: 'Remove All CSS Classes',
27656                 handler: function(a,b) {
27657                     
27658                     var c = Roo.get(editorcore.doc.body);
27659                     c.select('[class]').each(function(s) {
27660                         s.dom.removeAttribute('class');
27661                     });
27662                     editorcore.cleanWord();
27663                     editorcore.syncValue();
27664                 },
27665                 tabIndex:-1
27666             });
27667             
27668              cmenu.menu.items.push({
27669                 actiontype : 'tidy',
27670                 html: 'Tidy HTML Source',
27671                 handler: function(a,b) {
27672                     new Roo.htmleditor.Tidy(editorcore.doc.body);
27673                     editorcore.syncValue();
27674                 },
27675                 tabIndex:-1
27676             });
27677             
27678             
27679             tb.add(cmenu);
27680         }
27681          
27682         if (!this.disable.specialElements) {
27683             var semenu = {
27684                 text: "Other;",
27685                 cls: 'x-edit-none',
27686                 menu : {
27687                     items : []
27688                 }
27689             };
27690             for (var i =0; i < this.specialElements.length; i++) {
27691                 semenu.menu.items.push(
27692                     Roo.apply({ 
27693                         handler: function(a,b) {
27694                             editor.insertAtCursor(this.ihtml);
27695                         }
27696                     }, this.specialElements[i])
27697                 );
27698                     
27699             }
27700             
27701             tb.add(semenu);
27702             
27703             
27704         }
27705          
27706         
27707         if (this.btns) {
27708             for(var i =0; i< this.btns.length;i++) {
27709                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27710                 b.cls =  'x-edit-none';
27711                 
27712                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27713                     b.cls += ' x-init-enable';
27714                 }
27715                 
27716                 b.scope = editorcore;
27717                 tb.add(b);
27718             }
27719         
27720         }
27721         
27722         
27723         
27724         // disable everything...
27725         
27726         this.tb.items.each(function(item){
27727             
27728            if(
27729                 item.id != editorcore.frameId+ '-sourceedit' && 
27730                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27731             ){
27732                 
27733                 item.disable();
27734             }
27735         });
27736         this.rendered = true;
27737         
27738         // the all the btns;
27739         editor.on('editorevent', this.updateToolbar, this);
27740         // other toolbars need to implement this..
27741         //editor.on('editmodechange', this.updateToolbar, this);
27742     },
27743     
27744     
27745     relayBtnCmd : function(btn) {
27746         this.editorcore.relayCmd(btn.cmd);
27747     },
27748     // private used internally
27749     createLink : function(){
27750         //Roo.log("create link?");
27751         var ec = this.editorcore;
27752         (function() { 
27753             Roo.MessageBox.prompt("Add Link URL",this.createLinkText, function(btn, url) {
27754                 if (btn != 'ok') {
27755                     return;
27756                 }
27757                 if(url && url != 'http:/'+'/'){
27758                     ec.relayCmd('createlink', url);
27759                 }
27760             });
27761     }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
27762         
27763     },
27764
27765     
27766     /**
27767      * Protected method that will not generally be called directly. It triggers
27768      * a toolbar update by reading the markup state of the current selection in the editor.
27769      */
27770     updateToolbar: function(){
27771
27772         if(!this.editorcore.activated){
27773             this.editor.onFirstFocus();
27774             return;
27775         }
27776
27777         var btns = this.tb.items.map, 
27778             doc = this.editorcore.doc,
27779             frameId = this.editorcore.frameId;
27780
27781         if(!this.disable.font && !Roo.isSafari){
27782             /*
27783             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27784             if(name != this.fontSelect.dom.value){
27785                 this.fontSelect.dom.value = name;
27786             }
27787             */
27788         }
27789         if(!this.disable.format){
27790             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27791             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27792             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27793             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27794         }
27795         if(!this.disable.alignments){
27796             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27797             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27798             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27799         }
27800         if(!Roo.isSafari && !this.disable.lists){
27801             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27802             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27803         }
27804         
27805         var ans = this.editorcore.getAllAncestors();
27806         if (this.formatCombo) {
27807             
27808             
27809             var store = this.formatCombo.store;
27810             this.formatCombo.setValue("");
27811             for (var i =0; i < ans.length;i++) {
27812                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27813                     // select it..
27814                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27815                     break;
27816                 }
27817             }
27818         }
27819         
27820         
27821         
27822         // hides menus... - so this cant be on a menu...
27823         Roo.menu.MenuMgr.hideAll();
27824
27825         //this.editorsyncValue();
27826     },
27827    
27828     
27829     createFontOptions : function(){
27830         var buf = [], fs = this.fontFamilies, ff, lc;
27831         
27832         
27833         
27834         for(var i = 0, len = fs.length; i< len; i++){
27835             ff = fs[i];
27836             lc = ff.toLowerCase();
27837             buf.push(
27838                 '<option value="',lc,'" style="font-family:',ff,';"',
27839                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27840                     ff,
27841                 '</option>'
27842             );
27843         }
27844         return buf.join('');
27845     },
27846     
27847     toggleSourceEdit : function(sourceEditMode){
27848         
27849         Roo.log("toolbar toogle");
27850         if(sourceEditMode === undefined){
27851             sourceEditMode = !this.sourceEditMode;
27852         }
27853         this.sourceEditMode = sourceEditMode === true;
27854         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27855         // just toggle the button?
27856         if(btn.pressed !== this.sourceEditMode){
27857             btn.toggle(this.sourceEditMode);
27858             return;
27859         }
27860         
27861         if(sourceEditMode){
27862             Roo.log("disabling buttons");
27863             this.tb.items.each(function(item){
27864                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27865                     item.disable();
27866                 }
27867             });
27868           
27869         }else{
27870             Roo.log("enabling buttons");
27871             if(this.editorcore.initialized){
27872                 this.tb.items.each(function(item){
27873                     item.enable();
27874                 });
27875                 // initialize 'blocks'
27876                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
27877                     Roo.htmleditor.Block.factory(e).updateElement(e);
27878                 },this);
27879             
27880             }
27881             
27882         }
27883         Roo.log("calling toggole on editor");
27884         // tell the editor that it's been pressed..
27885         this.editor.toggleSourceEdit(sourceEditMode);
27886        
27887     },
27888      /**
27889      * Object collection of toolbar tooltips for the buttons in the editor. The key
27890      * is the command id associated with that button and the value is a valid QuickTips object.
27891      * For example:
27892 <pre><code>
27893 {
27894     bold : {
27895         title: 'Bold (Ctrl+B)',
27896         text: 'Make the selected text bold.',
27897         cls: 'x-html-editor-tip'
27898     },
27899     italic : {
27900         title: 'Italic (Ctrl+I)',
27901         text: 'Make the selected text italic.',
27902         cls: 'x-html-editor-tip'
27903     },
27904     ...
27905 </code></pre>
27906     * @type Object
27907      */
27908     buttonTips : {
27909         bold : {
27910             title: 'Bold (Ctrl+B)',
27911             text: 'Make the selected text bold.',
27912             cls: 'x-html-editor-tip'
27913         },
27914         italic : {
27915             title: 'Italic (Ctrl+I)',
27916             text: 'Make the selected text italic.',
27917             cls: 'x-html-editor-tip'
27918         },
27919         underline : {
27920             title: 'Underline (Ctrl+U)',
27921             text: 'Underline the selected text.',
27922             cls: 'x-html-editor-tip'
27923         },
27924         strikethrough : {
27925             title: 'Strikethrough',
27926             text: 'Strikethrough the selected text.',
27927             cls: 'x-html-editor-tip'
27928         },
27929         increasefontsize : {
27930             title: 'Grow Text',
27931             text: 'Increase the font size.',
27932             cls: 'x-html-editor-tip'
27933         },
27934         decreasefontsize : {
27935             title: 'Shrink Text',
27936             text: 'Decrease the font size.',
27937             cls: 'x-html-editor-tip'
27938         },
27939         backcolor : {
27940             title: 'Text Highlight Color',
27941             text: 'Change the background color of the selected text.',
27942             cls: 'x-html-editor-tip'
27943         },
27944         forecolor : {
27945             title: 'Font Color',
27946             text: 'Change the color of the selected text.',
27947             cls: 'x-html-editor-tip'
27948         },
27949         justifyleft : {
27950             title: 'Align Text Left',
27951             text: 'Align text to the left.',
27952             cls: 'x-html-editor-tip'
27953         },
27954         justifycenter : {
27955             title: 'Center Text',
27956             text: 'Center text in the editor.',
27957             cls: 'x-html-editor-tip'
27958         },
27959         justifyright : {
27960             title: 'Align Text Right',
27961             text: 'Align text to the right.',
27962             cls: 'x-html-editor-tip'
27963         },
27964         insertunorderedlist : {
27965             title: 'Bullet List',
27966             text: 'Start a bulleted list.',
27967             cls: 'x-html-editor-tip'
27968         },
27969         insertorderedlist : {
27970             title: 'Numbered List',
27971             text: 'Start a numbered list.',
27972             cls: 'x-html-editor-tip'
27973         },
27974         createlink : {
27975             title: 'Hyperlink',
27976             text: 'Make the selected text a hyperlink.',
27977             cls: 'x-html-editor-tip'
27978         },
27979         sourceedit : {
27980             title: 'Source Edit',
27981             text: 'Switch to source editing mode.',
27982             cls: 'x-html-editor-tip'
27983         }
27984     },
27985     // private
27986     onDestroy : function(){
27987         if(this.rendered){
27988             
27989             this.tb.items.each(function(item){
27990                 if(item.menu){
27991                     item.menu.removeAll();
27992                     if(item.menu.el){
27993                         item.menu.el.destroy();
27994                     }
27995                 }
27996                 item.destroy();
27997             });
27998              
27999         }
28000     },
28001     onFirstFocus: function() {
28002         this.tb.items.each(function(item){
28003            item.enable();
28004         });
28005     }
28006 };
28007
28008
28009
28010
28011 // <script type="text/javascript">
28012 /*
28013  * Based on
28014  * Ext JS Library 1.1.1
28015  * Copyright(c) 2006-2007, Ext JS, LLC.
28016  *  
28017  
28018  */
28019
28020  
28021 /**
28022  * @class Roo.form.HtmlEditor.ToolbarContext
28023  * Context Toolbar
28024  * 
28025  * Usage:
28026  *
28027  new Roo.form.HtmlEditor({
28028     ....
28029     toolbars : [
28030         { xtype: 'ToolbarStandard', styles : {} }
28031         { xtype: 'ToolbarContext', disable : {} }
28032     ]
28033 })
28034
28035      
28036  * 
28037  * @config : {Object} disable List of elements to disable.. (not done yet.)
28038  * @config : {Object} styles  Map of styles available.
28039  * 
28040  */
28041
28042 Roo.form.HtmlEditor.ToolbarContext = function(config)
28043 {
28044     
28045     Roo.apply(this, config);
28046     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28047     // dont call parent... till later.
28048     this.styles = this.styles || {};
28049 }
28050
28051  
28052
28053 Roo.form.HtmlEditor.ToolbarContext.types = {
28054     'IMG' : [
28055         {
28056             name : 'width',
28057             title: "Width",
28058             width: 40
28059         },
28060         {
28061             name : 'height',
28062             title: "Height",
28063             width: 40
28064         },
28065         {
28066             name : 'align',
28067             title: "Align",
28068             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28069             width : 80
28070             
28071         },
28072         {
28073             name : 'border',
28074             title: "Border",
28075             width: 40
28076         },
28077         {
28078             name : 'alt',
28079             title: "Alt",
28080             width: 120
28081         },
28082         {
28083             name : 'src',
28084             title: "Src",
28085             width: 220
28086         }
28087         
28088     ],
28089     
28090     'FIGURE' : [
28091         {
28092             name : 'align',
28093             title: "Align",
28094             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28095             width : 80  
28096         }
28097     ],
28098     'A' : [
28099         {
28100             name : 'name',
28101             title: "Name",
28102             width: 50
28103         },
28104         {
28105             name : 'target',
28106             title: "Target",
28107             width: 120
28108         },
28109         {
28110             name : 'href',
28111             title: "Href",
28112             width: 220
28113         } // border?
28114         
28115     ],
28116     
28117     'INPUT' : [
28118         {
28119             name : 'name',
28120             title: "name",
28121             width: 120
28122         },
28123         {
28124             name : 'value',
28125             title: "Value",
28126             width: 120
28127         },
28128         {
28129             name : 'width',
28130             title: "Width",
28131             width: 40
28132         }
28133     ],
28134     'LABEL' : [
28135          {
28136             name : 'for',
28137             title: "For",
28138             width: 120
28139         }
28140     ],
28141     'TEXTAREA' : [
28142         {
28143             name : 'name',
28144             title: "name",
28145             width: 120
28146         },
28147         {
28148             name : 'rows',
28149             title: "Rows",
28150             width: 20
28151         },
28152         {
28153             name : 'cols',
28154             title: "Cols",
28155             width: 20
28156         }
28157     ],
28158     'SELECT' : [
28159         {
28160             name : 'name',
28161             title: "name",
28162             width: 120
28163         },
28164         {
28165             name : 'selectoptions',
28166             title: "Options",
28167             width: 200
28168         }
28169     ],
28170     
28171     // should we really allow this??
28172     // should this just be 
28173     'BODY' : [
28174         
28175         {
28176             name : 'title',
28177             title: "Title",
28178             width: 200,
28179             disabled : true
28180         }
28181     ],
28182  
28183     '*' : [
28184         // empty.
28185     ]
28186
28187 };
28188
28189 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28190 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28191
28192 Roo.form.HtmlEditor.ToolbarContext.options = {
28193         'font-family'  : [ 
28194                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28195                 [ 'Courier New', 'Courier New'],
28196                 [ 'Tahoma', 'Tahoma'],
28197                 [ 'Times New Roman,serif', 'Times'],
28198                 [ 'Verdana','Verdana' ]
28199         ]
28200 };
28201
28202 // fixme - these need to be configurable..
28203  
28204
28205 //Roo.form.HtmlEditor.ToolbarContext.types
28206
28207
28208 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28209     
28210     tb: false,
28211     
28212     rendered: false,
28213     
28214     editor : false,
28215     editorcore : false,
28216     /**
28217      * @cfg {Object} disable  List of toolbar elements to disable
28218          
28219      */
28220     disable : false,
28221     /**
28222      * @cfg {Object} styles List of styles 
28223      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28224      *
28225      * These must be defined in the page, so they get rendered correctly..
28226      * .headline { }
28227      * TD.underline { }
28228      * 
28229      */
28230     styles : false,
28231     
28232     options: false,
28233     
28234     toolbars : false,
28235     
28236     init : function(editor)
28237     {
28238         this.editor = editor;
28239         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28240         var editorcore = this.editorcore;
28241         
28242         var fid = editorcore.frameId;
28243         var etb = this;
28244         function btn(id, toggle, handler){
28245             var xid = fid + '-'+ id ;
28246             return {
28247                 id : xid,
28248                 cmd : id,
28249                 cls : 'x-btn-icon x-edit-'+id,
28250                 enableToggle:toggle !== false,
28251                 scope: editorcore, // was editor...
28252                 handler:handler||editorcore.relayBtnCmd,
28253                 clickEvent:'mousedown',
28254                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28255                 tabIndex:-1
28256             };
28257         }
28258         // create a new element.
28259         var wdiv = editor.wrap.createChild({
28260                 tag: 'div'
28261             }, editor.wrap.dom.firstChild.nextSibling, true);
28262         
28263         // can we do this more than once??
28264         
28265          // stop form submits
28266       
28267  
28268         // disable everything...
28269         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28270         this.toolbars = {};
28271         // block toolbars are built in updateToolbar when needed.
28272         for (var i in  ty) {
28273             
28274             this.toolbars[i] = this.buildToolbar(ty[i],i);
28275         }
28276         this.tb = this.toolbars.BODY;
28277         this.tb.el.show();
28278         this.buildFooter();
28279         this.footer.show();
28280         editor.on('hide', function( ) { this.footer.hide() }, this);
28281         editor.on('show', function( ) { this.footer.show() }, this);
28282         
28283          
28284         this.rendered = true;
28285         
28286         // the all the btns;
28287         editor.on('editorevent', this.updateToolbar, this);
28288         // other toolbars need to implement this..
28289         //editor.on('editmodechange', this.updateToolbar, this);
28290     },
28291     
28292     
28293     
28294     /**
28295      * Protected method that will not generally be called directly. It triggers
28296      * a toolbar update by reading the markup state of the current selection in the editor.
28297      *
28298      * Note you can force an update by calling on('editorevent', scope, false)
28299      */
28300     updateToolbar: function(editor ,ev, sel)
28301     {
28302         
28303         if (ev) {
28304             ev.stopEvent(); // se if we can stop this looping with mutiple events.
28305         }
28306         
28307         //Roo.log(ev);
28308         // capture mouse up - this is handy for selecting images..
28309         // perhaps should go somewhere else...
28310         if(!this.editorcore.activated){
28311              this.editor.onFirstFocus();
28312             return;
28313         }
28314         //Roo.log(ev ? ev.target : 'NOTARGET');
28315         
28316         
28317         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28318         // selectNode - might want to handle IE?
28319         
28320         
28321         
28322         if (ev &&
28323             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28324             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28325             // they have click on an image...
28326             // let's see if we can change the selection...
28327             sel = ev.target;
28328             
28329             // this triggers looping?
28330             //this.editorcore.selectNode(sel);
28331              
28332         }
28333         
28334         // this forces an id..
28335         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28336              e.classList.remove('roo-ed-selection');
28337         });
28338         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28339         //Roo.get(node).addClass('roo-ed-selection');
28340       
28341         //var updateFooter = sel ? false : true; 
28342         
28343         
28344         var ans = this.editorcore.getAllAncestors();
28345         
28346         // pick
28347         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28348         
28349         if (!sel) { 
28350             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28351             sel = sel ? sel : this.editorcore.doc.body;
28352             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28353             
28354         }
28355         
28356         var tn = sel.tagName.toUpperCase();
28357         var lastSel = this.tb.selectedNode;
28358         this.tb.selectedNode = sel;
28359         var left_label = tn;
28360         
28361         // ok see if we are editing a block?
28362         
28363         var db = false;
28364         // you are not actually selecting the block.
28365         if (sel && sel.hasAttribute('data-block')) {
28366             db = sel;
28367         } else if (sel && sel.closest('[data-block]')) {
28368             
28369             db = sel.closest('[data-block]');
28370             //var cepar = sel.closest('[contenteditable=true]');
28371             //if (db && cepar && cepar.tagName != 'BODY') {
28372             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28373             //}   
28374         }
28375         
28376         
28377         var block = false;
28378         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28379         if (db && this.editorcore.enableBlocks) {
28380             block = Roo.htmleditor.Block.factory(db);
28381             
28382             
28383             if (block) {
28384                  db.className = (
28385                         db.classList.length > 0  ? db.className + ' ' : ''
28386                     )  + 'roo-ed-selection';
28387                  
28388                  // since we removed it earlier... its not there..
28389                 tn = 'BLOCK.' + db.getAttribute('data-block');
28390                 
28391                 //this.editorcore.selectNode(db);
28392                 if (typeof(this.toolbars[tn]) == 'undefined') {
28393                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
28394                 }
28395                 this.toolbars[tn].selectedNode = db;
28396                 left_label = block.friendly_name;
28397                 ans = this.editorcore.getAllAncestors();
28398             }
28399             
28400                 
28401             
28402         }
28403         
28404         
28405         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28406             return; // no change?
28407         }
28408         
28409         
28410           
28411         this.tb.el.hide();
28412         ///console.log("show: " + tn);
28413         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28414         
28415         this.tb.el.show();
28416         // update name
28417         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
28418         
28419         
28420         // update attributes
28421         if (block && this.tb.fields) {
28422              
28423             this.tb.fields.each(function(e) {
28424                 e.setValue(block[e.name]);
28425             });
28426             
28427             
28428         } else  if (this.tb.fields && this.tb.selectedNode) {
28429             this.tb.fields.each( function(e) {
28430                 if (e.stylename) {
28431                     e.setValue(this.tb.selectedNode.style[e.stylename]);
28432                     return;
28433                 } 
28434                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28435             }, this);
28436             this.updateToolbarStyles(this.tb.selectedNode);  
28437         }
28438         
28439         
28440        
28441         Roo.menu.MenuMgr.hideAll();
28442
28443         
28444         
28445     
28446         // update the footer
28447         //
28448         this.updateFooter(ans);
28449              
28450     },
28451     
28452     updateToolbarStyles : function(sel)
28453     {
28454         var hasStyles = false;
28455         for(var i in this.styles) {
28456             hasStyles = true;
28457             break;
28458         }
28459         
28460         // update styles
28461         if (hasStyles && this.tb.hasStyles) { 
28462             var st = this.tb.fields.item(0);
28463             
28464             st.store.removeAll();
28465             var cn = sel.className.split(/\s+/);
28466             
28467             var avs = [];
28468             if (this.styles['*']) {
28469                 
28470                 Roo.each(this.styles['*'], function(v) {
28471                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28472                 });
28473             }
28474             if (this.styles[tn]) { 
28475                 Roo.each(this.styles[tn], function(v) {
28476                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28477                 });
28478             }
28479             
28480             st.store.loadData(avs);
28481             st.collapse();
28482             st.setValue(cn);
28483         }
28484     },
28485     
28486      
28487     updateFooter : function(ans)
28488     {
28489         var html = '';
28490         if (ans === false) {
28491             this.footDisp.dom.innerHTML = '';
28492             return;
28493         }
28494         
28495         this.footerEls = ans.reverse();
28496         Roo.each(this.footerEls, function(a,i) {
28497             if (!a) { return; }
28498             html += html.length ? ' &gt; '  :  '';
28499             
28500             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28501             
28502         });
28503        
28504         // 
28505         var sz = this.footDisp.up('td').getSize();
28506         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28507         this.footDisp.dom.style.marginLeft = '5px';
28508         
28509         this.footDisp.dom.style.overflow = 'hidden';
28510         
28511         this.footDisp.dom.innerHTML = html;
28512             
28513         
28514     },
28515    
28516        
28517     // private
28518     onDestroy : function(){
28519         if(this.rendered){
28520             
28521             this.tb.items.each(function(item){
28522                 if(item.menu){
28523                     item.menu.removeAll();
28524                     if(item.menu.el){
28525                         item.menu.el.destroy();
28526                     }
28527                 }
28528                 item.destroy();
28529             });
28530              
28531         }
28532     },
28533     onFirstFocus: function() {
28534         // need to do this for all the toolbars..
28535         this.tb.items.each(function(item){
28536            item.enable();
28537         });
28538     },
28539     buildToolbar: function(tlist, nm, friendly_name, block)
28540     {
28541         var editor = this.editor;
28542         var editorcore = this.editorcore;
28543          // create a new element.
28544         var wdiv = editor.wrap.createChild({
28545                 tag: 'div'
28546             }, editor.wrap.dom.firstChild.nextSibling, true);
28547         
28548        
28549         var tb = new Roo.Toolbar(wdiv);
28550         ///this.tb = tb; // << this sets the active toolbar..
28551         if (tlist === false && block) {
28552             tlist = block.contextMenu(this);
28553         }
28554         
28555         tb.hasStyles = false;
28556         tb.name = nm;
28557         
28558         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
28559         
28560         var styles = Array.from(this.styles);
28561         
28562         
28563         // styles...
28564         if (styles && styles.length) {
28565             tb.hasStyles = true;
28566             // this needs a multi-select checkbox...
28567             tb.addField( new Roo.form.ComboBox({
28568                 store: new Roo.data.SimpleStore({
28569                     id : 'val',
28570                     fields: ['val', 'selected'],
28571                     data : [] 
28572                 }),
28573                 name : '-roo-edit-className',
28574                 attrname : 'className',
28575                 displayField: 'val',
28576                 typeAhead: false,
28577                 mode: 'local',
28578                 editable : false,
28579                 triggerAction: 'all',
28580                 emptyText:'Select Style',
28581                 selectOnFocus:true,
28582                 width: 130,
28583                 listeners : {
28584                     'select': function(c, r, i) {
28585                         // initial support only for on class per el..
28586                         tb.selectedNode.className =  r ? r.get('val') : '';
28587                         editorcore.syncValue();
28588                     }
28589                 }
28590     
28591             }));
28592         }
28593         
28594         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28595         
28596         
28597         for (var i = 0; i < tlist.length; i++) {
28598             
28599             // newer versions will use xtype cfg to create menus.
28600             if (typeof(tlist[i].xtype) != 'undefined') {
28601                 
28602                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28603                 
28604                 
28605                 continue;
28606             }
28607             
28608             var item = tlist[i];
28609             tb.add(item.title + ":&nbsp;");
28610             
28611             
28612             //optname == used so you can configure the options available..
28613             var opts = item.opts ? item.opts : false;
28614             if (item.optname) { // use the b
28615                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28616            
28617             }
28618             
28619             if (opts) {
28620                 // opts == pulldown..
28621                 tb.addField( new Roo.form.ComboBox({
28622                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28623                         id : 'val',
28624                         fields: ['val', 'display'],
28625                         data : opts  
28626                     }),
28627                     name : '-roo-edit-' + tlist[i].name,
28628                     
28629                     attrname : tlist[i].name,
28630                     stylename : item.style ? item.style : false,
28631                     
28632                     displayField: item.displayField ? item.displayField : 'val',
28633                     valueField :  'val',
28634                     typeAhead: false,
28635                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
28636                     editable : false,
28637                     triggerAction: 'all',
28638                     emptyText:'Select',
28639                     selectOnFocus:true,
28640                     width: item.width ? item.width  : 130,
28641                     listeners : {
28642                         'select': function(c, r, i) {
28643                              
28644                             
28645                             if (c.stylename) {
28646                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28647                                 editorcore.syncValue();
28648                                 return;
28649                             }
28650                             if (r === false) {
28651                                 tb.selectedNode.removeAttribute(c.attrname);
28652                                 editorcore.syncValue();
28653                                 return;
28654                             }
28655                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28656                             editorcore.syncValue();
28657                         }
28658                     }
28659
28660                 }));
28661                 continue;
28662                     
28663                  
28664                 /*
28665                 tb.addField( new Roo.form.TextField({
28666                     name: i,
28667                     width: 100,
28668                     //allowBlank:false,
28669                     value: ''
28670                 }));
28671                 continue;
28672                 */
28673             }
28674             tb.addField( new Roo.form.TextField({
28675                 name: '-roo-edit-' + tlist[i].name,
28676                 attrname : tlist[i].name,
28677                 
28678                 width: item.width,
28679                 //allowBlank:true,
28680                 value: '',
28681                 listeners: {
28682                     'change' : function(f, nv, ov) {
28683                         
28684                          
28685                         tb.selectedNode.setAttribute(f.attrname, nv);
28686                         editorcore.syncValue();
28687                     }
28688                 }
28689             }));
28690              
28691         }
28692         
28693         var _this = this;
28694         var show_delete = !block || block.deleteTitle !== false;
28695         if(nm == 'BODY'){
28696             show_delete = false;
28697             tb.addSeparator();
28698         
28699             tb.addButton( {
28700                 text: 'Stylesheets',
28701
28702                 listeners : {
28703                     click : function ()
28704                     {
28705                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28706                     }
28707                 }
28708             });
28709         }
28710         
28711         tb.addFill();
28712         if (show_delete) {
28713             tb.addButton({
28714                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28715         
28716                 listeners : {
28717                     click : function ()
28718                     {
28719                         var sn = tb.selectedNode;
28720                         if (block) {
28721                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28722                             
28723                         }
28724                         if (!sn) {
28725                             return;
28726                         }
28727                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
28728                         if (sn.hasAttribute('data-block')) {
28729                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
28730                             sn.parentNode.removeChild(sn);
28731                             
28732                         } else if (sn && sn.tagName != 'BODY') {
28733                             // remove and keep parents.
28734                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
28735                             a.replaceTag(sn);
28736                         }
28737                         
28738                         
28739                         var range = editorcore.createRange();
28740             
28741                         range.setStart(stn,0);
28742                         range.setEnd(stn,0); 
28743                         var selection = editorcore.getSelection();
28744                         selection.removeAllRanges();
28745                         selection.addRange(range);
28746                         
28747                         
28748                         //_this.updateToolbar(null, null, pn);
28749                         _this.updateToolbar(null, null, null);
28750                         _this.updateFooter(false);
28751                         
28752                     }
28753                 }
28754                 
28755                         
28756                     
28757                 
28758             });
28759         }    
28760         
28761         tb.el.on('click', function(e){
28762             e.preventDefault(); // what does this do?
28763         });
28764         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28765         tb.el.hide();
28766         
28767         // dont need to disable them... as they will get hidden
28768         return tb;
28769          
28770         
28771     },
28772     buildFooter : function()
28773     {
28774         
28775         var fel = this.editor.wrap.createChild();
28776         this.footer = new Roo.Toolbar(fel);
28777         // toolbar has scrolly on left / right?
28778         var footDisp= new Roo.Toolbar.Fill();
28779         var _t = this;
28780         this.footer.add(
28781             {
28782                 text : '&lt;',
28783                 xtype: 'Button',
28784                 handler : function() {
28785                     _t.footDisp.scrollTo('left',0,true)
28786                 }
28787             }
28788         );
28789         this.footer.add( footDisp );
28790         this.footer.add( 
28791             {
28792                 text : '&gt;',
28793                 xtype: 'Button',
28794                 handler : function() {
28795                     // no animation..
28796                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28797                 }
28798             }
28799         );
28800         var fel = Roo.get(footDisp.el);
28801         fel.addClass('x-editor-context');
28802         this.footDispWrap = fel; 
28803         this.footDispWrap.overflow  = 'hidden';
28804         
28805         this.footDisp = fel.createChild();
28806         this.footDispWrap.on('click', this.onContextClick, this)
28807         
28808         
28809     },
28810     // when the footer contect changes
28811     onContextClick : function (ev,dom)
28812     {
28813         ev.preventDefault();
28814         var  cn = dom.className;
28815         //Roo.log(cn);
28816         if (!cn.match(/x-ed-loc-/)) {
28817             return;
28818         }
28819         var n = cn.split('-').pop();
28820         var ans = this.footerEls;
28821         var sel = ans[n];
28822         
28823         this.editorcore.selectNode(sel);
28824         
28825         
28826         this.updateToolbar(null, null, sel);
28827         
28828         
28829     }
28830     
28831     
28832     
28833     
28834     
28835 });
28836
28837
28838
28839
28840
28841 /*
28842  * Based on:
28843  * Ext JS Library 1.1.1
28844  * Copyright(c) 2006-2007, Ext JS, LLC.
28845  *
28846  * Originally Released Under LGPL - original licence link has changed is not relivant.
28847  *
28848  * Fork - LGPL
28849  * <script type="text/javascript">
28850  */
28851  
28852 /**
28853  * @class Roo.form.BasicForm
28854  * @extends Roo.util.Observable
28855  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28856  * @constructor
28857  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28858  * @param {Object} config Configuration options
28859  */
28860 Roo.form.BasicForm = function(el, config){
28861     this.allItems = [];
28862     this.childForms = [];
28863     Roo.apply(this, config);
28864     /*
28865      * The Roo.form.Field items in this form.
28866      * @type MixedCollection
28867      */
28868      
28869      
28870     this.items = new Roo.util.MixedCollection(false, function(o){
28871         return o.id || (o.id = Roo.id());
28872     });
28873     this.addEvents({
28874         /**
28875          * @event beforeaction
28876          * Fires before any action is performed. Return false to cancel the action.
28877          * @param {Form} this
28878          * @param {Action} action The action to be performed
28879          */
28880         beforeaction: true,
28881         /**
28882          * @event actionfailed
28883          * Fires when an action fails.
28884          * @param {Form} this
28885          * @param {Action} action The action that failed
28886          */
28887         actionfailed : true,
28888         /**
28889          * @event actioncomplete
28890          * Fires when an action is completed.
28891          * @param {Form} this
28892          * @param {Action} action The action that completed
28893          */
28894         actioncomplete : true
28895     });
28896     if(el){
28897         this.initEl(el);
28898     }
28899     Roo.form.BasicForm.superclass.constructor.call(this);
28900     
28901     Roo.form.BasicForm.popover.apply();
28902 };
28903
28904 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28905     /**
28906      * @cfg {String} method
28907      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28908      */
28909     /**
28910      * @cfg {DataReader} reader
28911      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28912      * This is optional as there is built-in support for processing JSON.
28913      */
28914     /**
28915      * @cfg {DataReader} errorReader
28916      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28917      * This is completely optional as there is built-in support for processing JSON.
28918      */
28919     /**
28920      * @cfg {String} url
28921      * The URL to use for form actions if one isn't supplied in the action options.
28922      */
28923     /**
28924      * @cfg {Boolean} fileUpload
28925      * Set to true if this form is a file upload.
28926      */
28927      
28928     /**
28929      * @cfg {Object} baseParams
28930      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28931      */
28932      /**
28933      
28934     /**
28935      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28936      */
28937     timeout: 30,
28938
28939     // private
28940     activeAction : null,
28941
28942     /**
28943      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28944      * or setValues() data instead of when the form was first created.
28945      */
28946     trackResetOnLoad : false,
28947     
28948     
28949     /**
28950      * childForms - used for multi-tab forms
28951      * @type {Array}
28952      */
28953     childForms : false,
28954     
28955     /**
28956      * allItems - full list of fields.
28957      * @type {Array}
28958      */
28959     allItems : false,
28960     
28961     /**
28962      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28963      * element by passing it or its id or mask the form itself by passing in true.
28964      * @type Mixed
28965      */
28966     waitMsgTarget : false,
28967     
28968     /**
28969      * @type Boolean
28970      */
28971     disableMask : false,
28972     
28973     /**
28974      * @cfg {Boolean} errorMask (true|false) default false
28975      */
28976     errorMask : false,
28977     
28978     /**
28979      * @cfg {Number} maskOffset Default 100
28980      */
28981     maskOffset : 100,
28982
28983     // private
28984     initEl : function(el){
28985         this.el = Roo.get(el);
28986         this.id = this.el.id || Roo.id();
28987         this.el.on('submit', this.onSubmit, this);
28988         this.el.addClass('x-form');
28989     },
28990
28991     // private
28992     onSubmit : function(e){
28993         e.stopEvent();
28994     },
28995
28996     /**
28997      * Returns true if client-side validation on the form is successful.
28998      * @return Boolean
28999      */
29000     isValid : function(){
29001         var valid = true;
29002         var target = false;
29003         this.items.each(function(f){
29004             if(f.validate()){
29005                 return;
29006             }
29007             
29008             valid = false;
29009                 
29010             if(!target && f.el.isVisible(true)){
29011                 target = f;
29012             }
29013         });
29014         
29015         if(this.errorMask && !valid){
29016             Roo.form.BasicForm.popover.mask(this, target);
29017         }
29018         
29019         return valid;
29020     },
29021     /**
29022      * Returns array of invalid form fields.
29023      * @return Array
29024      */
29025     
29026     invalidFields : function()
29027     {
29028         var ret = [];
29029         this.items.each(function(f){
29030             if(f.validate()){
29031                 return;
29032             }
29033             ret.push(f);
29034             
29035         });
29036         
29037         return ret;
29038     },
29039     
29040     
29041     /**
29042      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
29043      * @return Boolean
29044      */
29045     isDirty : function(){
29046         var dirty = false;
29047         this.items.each(function(f){
29048            if(f.isDirty()){
29049                dirty = true;
29050                return false;
29051            }
29052         });
29053         return dirty;
29054     },
29055     
29056     /**
29057      * Returns true if any fields in this form have changed since their original load. (New version)
29058      * @return Boolean
29059      */
29060     
29061     hasChanged : function()
29062     {
29063         var dirty = false;
29064         this.items.each(function(f){
29065            if(f.hasChanged()){
29066                dirty = true;
29067                return false;
29068            }
29069         });
29070         return dirty;
29071         
29072     },
29073     /**
29074      * Resets all hasChanged to 'false' -
29075      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29076      * So hasChanged storage is only to be used for this purpose
29077      * @return Boolean
29078      */
29079     resetHasChanged : function()
29080     {
29081         this.items.each(function(f){
29082            f.resetHasChanged();
29083         });
29084         
29085     },
29086     
29087     
29088     /**
29089      * Performs a predefined action (submit or load) or custom actions you define on this form.
29090      * @param {String} actionName The name of the action type
29091      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
29092      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29093      * accept other config options):
29094      * <pre>
29095 Property          Type             Description
29096 ----------------  ---------------  ----------------------------------------------------------------------------------
29097 url               String           The url for the action (defaults to the form's url)
29098 method            String           The form method to use (defaults to the form's method, or POST if not defined)
29099 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
29100 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
29101                                    validate the form on the client (defaults to false)
29102      * </pre>
29103      * @return {BasicForm} this
29104      */
29105     doAction : function(action, options){
29106         if(typeof action == 'string'){
29107             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29108         }
29109         if(this.fireEvent('beforeaction', this, action) !== false){
29110             this.beforeAction(action);
29111             action.run.defer(100, action);
29112         }
29113         return this;
29114     },
29115
29116     /**
29117      * Shortcut to do a submit action.
29118      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29119      * @return {BasicForm} this
29120      */
29121     submit : function(options){
29122         this.doAction('submit', options);
29123         return this;
29124     },
29125
29126     /**
29127      * Shortcut to do a load action.
29128      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29129      * @return {BasicForm} this
29130      */
29131     load : function(options){
29132         this.doAction('load', options);
29133         return this;
29134     },
29135
29136     /**
29137      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29138      * @param {Record} record The record to edit
29139      * @return {BasicForm} this
29140      */
29141     updateRecord : function(record){
29142         record.beginEdit();
29143         var fs = record.fields;
29144         fs.each(function(f){
29145             var field = this.findField(f.name);
29146             if(field){
29147                 record.set(f.name, field.getValue());
29148             }
29149         }, this);
29150         record.endEdit();
29151         return this;
29152     },
29153
29154     /**
29155      * Loads an Roo.data.Record into this form.
29156      * @param {Record} record The record to load
29157      * @return {BasicForm} this
29158      */
29159     loadRecord : function(record){
29160         this.setValues(record.data);
29161         return this;
29162     },
29163
29164     // private
29165     beforeAction : function(action){
29166         var o = action.options;
29167         
29168         if(!this.disableMask) {
29169             if(this.waitMsgTarget === true){
29170                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29171             }else if(this.waitMsgTarget){
29172                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29173                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29174             }else {
29175                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29176             }
29177         }
29178         
29179          
29180     },
29181
29182     // private
29183     afterAction : function(action, success){
29184         this.activeAction = null;
29185         var o = action.options;
29186         
29187         if(!this.disableMask) {
29188             if(this.waitMsgTarget === true){
29189                 this.el.unmask();
29190             }else if(this.waitMsgTarget){
29191                 this.waitMsgTarget.unmask();
29192             }else{
29193                 Roo.MessageBox.updateProgress(1);
29194                 Roo.MessageBox.hide();
29195             }
29196         }
29197         
29198         if(success){
29199             if(o.reset){
29200                 this.reset();
29201             }
29202             Roo.callback(o.success, o.scope, [this, action]);
29203             this.fireEvent('actioncomplete', this, action);
29204             
29205         }else{
29206             
29207             // failure condition..
29208             // we have a scenario where updates need confirming.
29209             // eg. if a locking scenario exists..
29210             // we look for { errors : { needs_confirm : true }} in the response.
29211             if (
29212                 (typeof(action.result) != 'undefined')  &&
29213                 (typeof(action.result.errors) != 'undefined')  &&
29214                 (typeof(action.result.errors.needs_confirm) != 'undefined')
29215            ){
29216                 var _t = this;
29217                 Roo.MessageBox.confirm(
29218                     "Change requires confirmation",
29219                     action.result.errorMsg,
29220                     function(r) {
29221                         if (r != 'yes') {
29222                             return;
29223                         }
29224                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
29225                     }
29226                     
29227                 );
29228                 
29229                 
29230                 
29231                 return;
29232             }
29233             
29234             Roo.callback(o.failure, o.scope, [this, action]);
29235             // show an error message if no failed handler is set..
29236             if (!this.hasListener('actionfailed')) {
29237                 Roo.MessageBox.alert("Error",
29238                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29239                         action.result.errorMsg :
29240                         "Saving Failed, please check your entries or try again"
29241                 );
29242             }
29243             
29244             this.fireEvent('actionfailed', this, action);
29245         }
29246         
29247     },
29248
29249     /**
29250      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29251      * @param {String} id The value to search for
29252      * @return Field
29253      */
29254     findField : function(id){
29255         var field = this.items.get(id);
29256         if(!field){
29257             this.items.each(function(f){
29258                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29259                     field = f;
29260                     return false;
29261                 }
29262             });
29263         }
29264         return field || null;
29265     },
29266
29267     /**
29268      * Add a secondary form to this one, 
29269      * Used to provide tabbed forms. One form is primary, with hidden values 
29270      * which mirror the elements from the other forms.
29271      * 
29272      * @param {Roo.form.Form} form to add.
29273      * 
29274      */
29275     addForm : function(form)
29276     {
29277        
29278         if (this.childForms.indexOf(form) > -1) {
29279             // already added..
29280             return;
29281         }
29282         this.childForms.push(form);
29283         var n = '';
29284         Roo.each(form.allItems, function (fe) {
29285             
29286             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29287             if (this.findField(n)) { // already added..
29288                 return;
29289             }
29290             var add = new Roo.form.Hidden({
29291                 name : n
29292             });
29293             add.render(this.el);
29294             
29295             this.add( add );
29296         }, this);
29297         
29298     },
29299     /**
29300      * Mark fields in this form invalid in bulk.
29301      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29302      * @return {BasicForm} this
29303      */
29304     markInvalid : function(errors){
29305         if(errors instanceof Array){
29306             for(var i = 0, len = errors.length; i < len; i++){
29307                 var fieldError = errors[i];
29308                 var f = this.findField(fieldError.id);
29309                 if(f){
29310                     f.markInvalid(fieldError.msg);
29311                 }
29312             }
29313         }else{
29314             var field, id;
29315             for(id in errors){
29316                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29317                     field.markInvalid(errors[id]);
29318                 }
29319             }
29320         }
29321         Roo.each(this.childForms || [], function (f) {
29322             f.markInvalid(errors);
29323         });
29324         
29325         return this;
29326     },
29327
29328     /**
29329      * Set values for fields in this form in bulk.
29330      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29331      * @return {BasicForm} this
29332      */
29333     setValues : function(values){
29334         if(values instanceof Array){ // array of objects
29335             for(var i = 0, len = values.length; i < len; i++){
29336                 var v = values[i];
29337                 var f = this.findField(v.id);
29338                 if(f){
29339                     f.setValue(v.value);
29340                     if(this.trackResetOnLoad){
29341                         f.originalValue = f.getValue();
29342                     }
29343                 }
29344             }
29345         }else{ // object hash
29346             var field, id;
29347             for(id in values){
29348                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29349                     
29350                     if (field.setFromData && 
29351                         field.valueField && 
29352                         field.displayField &&
29353                         // combos' with local stores can 
29354                         // be queried via setValue()
29355                         // to set their value..
29356                         (field.store && !field.store.isLocal)
29357                         ) {
29358                         // it's a combo
29359                         var sd = { };
29360                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29361                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29362                         field.setFromData(sd);
29363                         
29364                     } else {
29365                         field.setValue(values[id]);
29366                     }
29367                     
29368                     
29369                     if(this.trackResetOnLoad){
29370                         field.originalValue = field.getValue();
29371                     }
29372                 }
29373             }
29374         }
29375         this.resetHasChanged();
29376         
29377         
29378         Roo.each(this.childForms || [], function (f) {
29379             f.setValues(values);
29380             f.resetHasChanged();
29381         });
29382                 
29383         return this;
29384     },
29385  
29386     /**
29387      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29388      * they are returned as an array.
29389      * @param {Boolean} asString
29390      * @return {Object}
29391      */
29392     getValues : function(asString)
29393     {
29394         if (this.childForms) {
29395             // copy values from the child forms
29396             Roo.each(this.childForms, function (f) {
29397                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29398             }, this);
29399         }
29400         
29401         // use formdata
29402         if (typeof(FormData) != 'undefined' && asString !== true) {
29403             // this relies on a 'recent' version of chrome apparently...
29404             try {
29405                 var fd = (new FormData(this.el.dom)).entries();
29406                 var ret = {};
29407                 var ent = fd.next();
29408                 while (!ent.done) {
29409                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29410                     ent = fd.next();
29411                 };
29412                 return ret;
29413             } catch(e) {
29414                 
29415             }
29416             
29417         }
29418         
29419         
29420         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29421         if(asString === true){
29422             return fs;
29423         }
29424         return Roo.urlDecode(fs);
29425     },
29426     
29427     /**
29428      * Returns the fields in this form as an object with key/value pairs. 
29429      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29430      * Normally this will not return readOnly data 
29431      * @param {Boolean} with_readonly return readonly field data.
29432      * @return {Object}
29433      */
29434     getFieldValues : function(with_readonly)
29435     {
29436         if (this.childForms) {
29437             // copy values from the child forms
29438             // should this call getFieldValues - probably not as we do not currently copy
29439             // hidden fields when we generate..
29440             Roo.each(this.childForms, function (f) {
29441                 this.setValues(f.getFieldValues());
29442             }, this);
29443         }
29444         
29445         var ret = {};
29446         this.items.each(function(f){
29447             
29448             if (f.readOnly && with_readonly !== true) {
29449                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29450                         // if a subform contains a copy of them.
29451                         // if you have subforms with the same editable data, you will need to copy the data back
29452                         // and forth.
29453             }
29454             
29455             if (!f.getName()) {
29456                 return;
29457             }
29458             var v = f.getValue();
29459             if (f.inputType =='radio') {
29460                 if (typeof(ret[f.getName()]) == 'undefined') {
29461                     ret[f.getName()] = ''; // empty..
29462                 }
29463                 
29464                 if (!f.el.dom.checked) {
29465                     return;
29466                     
29467                 }
29468                 v = f.el.dom.value;
29469                 
29470             }
29471             
29472             // not sure if this supported any more..
29473             if ((typeof(v) == 'object') && f.getRawValue) {
29474                 v = f.getRawValue() ; // dates..
29475             }
29476             // combo boxes where name != hiddenName...
29477             if (f.name != f.getName()) {
29478                 ret[f.name] = f.getRawValue();
29479             }
29480             ret[f.getName()] = v;
29481         });
29482         
29483         return ret;
29484     },
29485
29486     /**
29487      * Clears all invalid messages in this form.
29488      * @return {BasicForm} this
29489      */
29490     clearInvalid : function(){
29491         this.items.each(function(f){
29492            f.clearInvalid();
29493         });
29494         
29495         Roo.each(this.childForms || [], function (f) {
29496             f.clearInvalid();
29497         });
29498         
29499         
29500         return this;
29501     },
29502
29503     /**
29504      * Resets this form.
29505      * @return {BasicForm} this
29506      */
29507     reset : function(){
29508         this.items.each(function(f){
29509             f.reset();
29510         });
29511         
29512         Roo.each(this.childForms || [], function (f) {
29513             f.reset();
29514         });
29515         this.resetHasChanged();
29516         
29517         return this;
29518     },
29519
29520     /**
29521      * Add Roo.form components to this form.
29522      * @param {Field} field1
29523      * @param {Field} field2 (optional)
29524      * @param {Field} etc (optional)
29525      * @return {BasicForm} this
29526      */
29527     add : function(){
29528         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29529         return this;
29530     },
29531
29532
29533     /**
29534      * Removes a field from the items collection (does NOT remove its markup).
29535      * @param {Field} field
29536      * @return {BasicForm} this
29537      */
29538     remove : function(field){
29539         this.items.remove(field);
29540         return this;
29541     },
29542
29543     /**
29544      * Looks at the fields in this form, checks them for an id attribute,
29545      * and calls applyTo on the existing dom element with that id.
29546      * @return {BasicForm} this
29547      */
29548     render : function(){
29549         this.items.each(function(f){
29550             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29551                 f.applyTo(f.id);
29552             }
29553         });
29554         return this;
29555     },
29556
29557     /**
29558      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29559      * @param {Object} values
29560      * @return {BasicForm} this
29561      */
29562     applyToFields : function(o){
29563         this.items.each(function(f){
29564            Roo.apply(f, o);
29565         });
29566         return this;
29567     },
29568
29569     /**
29570      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29571      * @param {Object} values
29572      * @return {BasicForm} this
29573      */
29574     applyIfToFields : function(o){
29575         this.items.each(function(f){
29576            Roo.applyIf(f, o);
29577         });
29578         return this;
29579     }
29580 });
29581
29582 // back compat
29583 Roo.BasicForm = Roo.form.BasicForm;
29584
29585 Roo.apply(Roo.form.BasicForm, {
29586     
29587     popover : {
29588         
29589         padding : 5,
29590         
29591         isApplied : false,
29592         
29593         isMasked : false,
29594         
29595         form : false,
29596         
29597         target : false,
29598         
29599         intervalID : false,
29600         
29601         maskEl : false,
29602         
29603         apply : function()
29604         {
29605             if(this.isApplied){
29606                 return;
29607             }
29608             
29609             this.maskEl = {
29610                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29611                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29612                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29613                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29614             };
29615             
29616             this.maskEl.top.enableDisplayMode("block");
29617             this.maskEl.left.enableDisplayMode("block");
29618             this.maskEl.bottom.enableDisplayMode("block");
29619             this.maskEl.right.enableDisplayMode("block");
29620             
29621             Roo.get(document.body).on('click', function(){
29622                 this.unmask();
29623             }, this);
29624             
29625             Roo.get(document.body).on('touchstart', function(){
29626                 this.unmask();
29627             }, this);
29628             
29629             this.isApplied = true
29630         },
29631         
29632         mask : function(form, target)
29633         {
29634             this.form = form;
29635             
29636             this.target = target;
29637             
29638             if(!this.form.errorMask || !target.el){
29639                 return;
29640             }
29641             
29642             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29643             
29644             var ot = this.target.el.calcOffsetsTo(scrollable);
29645             
29646             var scrollTo = ot[1] - this.form.maskOffset;
29647             
29648             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29649             
29650             scrollable.scrollTo('top', scrollTo);
29651             
29652             var el = this.target.wrap || this.target.el;
29653             
29654             var box = el.getBox();
29655             
29656             this.maskEl.top.setStyle('position', 'absolute');
29657             this.maskEl.top.setStyle('z-index', 10000);
29658             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29659             this.maskEl.top.setLeft(0);
29660             this.maskEl.top.setTop(0);
29661             this.maskEl.top.show();
29662             
29663             this.maskEl.left.setStyle('position', 'absolute');
29664             this.maskEl.left.setStyle('z-index', 10000);
29665             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29666             this.maskEl.left.setLeft(0);
29667             this.maskEl.left.setTop(box.y - this.padding);
29668             this.maskEl.left.show();
29669
29670             this.maskEl.bottom.setStyle('position', 'absolute');
29671             this.maskEl.bottom.setStyle('z-index', 10000);
29672             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29673             this.maskEl.bottom.setLeft(0);
29674             this.maskEl.bottom.setTop(box.bottom + this.padding);
29675             this.maskEl.bottom.show();
29676
29677             this.maskEl.right.setStyle('position', 'absolute');
29678             this.maskEl.right.setStyle('z-index', 10000);
29679             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29680             this.maskEl.right.setLeft(box.right + this.padding);
29681             this.maskEl.right.setTop(box.y - this.padding);
29682             this.maskEl.right.show();
29683
29684             this.intervalID = window.setInterval(function() {
29685                 Roo.form.BasicForm.popover.unmask();
29686             }, 10000);
29687
29688             window.onwheel = function(){ return false;};
29689             
29690             (function(){ this.isMasked = true; }).defer(500, this);
29691             
29692         },
29693         
29694         unmask : function()
29695         {
29696             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29697                 return;
29698             }
29699             
29700             this.maskEl.top.setStyle('position', 'absolute');
29701             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29702             this.maskEl.top.hide();
29703
29704             this.maskEl.left.setStyle('position', 'absolute');
29705             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29706             this.maskEl.left.hide();
29707
29708             this.maskEl.bottom.setStyle('position', 'absolute');
29709             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29710             this.maskEl.bottom.hide();
29711
29712             this.maskEl.right.setStyle('position', 'absolute');
29713             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29714             this.maskEl.right.hide();
29715             
29716             window.onwheel = function(){ return true;};
29717             
29718             if(this.intervalID){
29719                 window.clearInterval(this.intervalID);
29720                 this.intervalID = false;
29721             }
29722             
29723             this.isMasked = false;
29724             
29725         }
29726         
29727     }
29728     
29729 });/*
29730  * Based on:
29731  * Ext JS Library 1.1.1
29732  * Copyright(c) 2006-2007, Ext JS, LLC.
29733  *
29734  * Originally Released Under LGPL - original licence link has changed is not relivant.
29735  *
29736  * Fork - LGPL
29737  * <script type="text/javascript">
29738  */
29739
29740 /**
29741  * @class Roo.form.Form
29742  * @extends Roo.form.BasicForm
29743  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29744  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29745  * @constructor
29746  * @param {Object} config Configuration options
29747  */
29748 Roo.form.Form = function(config){
29749     var xitems =  [];
29750     if (config.items) {
29751         xitems = config.items;
29752         delete config.items;
29753     }
29754    
29755     
29756     Roo.form.Form.superclass.constructor.call(this, null, config);
29757     this.url = this.url || this.action;
29758     if(!this.root){
29759         this.root = new Roo.form.Layout(Roo.applyIf({
29760             id: Roo.id()
29761         }, config));
29762     }
29763     this.active = this.root;
29764     /**
29765      * Array of all the buttons that have been added to this form via {@link addButton}
29766      * @type Array
29767      */
29768     this.buttons = [];
29769     this.allItems = [];
29770     this.addEvents({
29771         /**
29772          * @event clientvalidation
29773          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29774          * @param {Form} this
29775          * @param {Boolean} valid true if the form has passed client-side validation
29776          */
29777         clientvalidation: true,
29778         /**
29779          * @event rendered
29780          * Fires when the form is rendered
29781          * @param {Roo.form.Form} form
29782          */
29783         rendered : true
29784     });
29785     
29786     if (this.progressUrl) {
29787             // push a hidden field onto the list of fields..
29788             this.addxtype( {
29789                     xns: Roo.form, 
29790                     xtype : 'Hidden', 
29791                     name : 'UPLOAD_IDENTIFIER' 
29792             });
29793         }
29794         
29795     
29796     Roo.each(xitems, this.addxtype, this);
29797     
29798 };
29799
29800 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29801      /**
29802      * @cfg {Roo.Button} buttons[] buttons at bottom of form
29803      */
29804     
29805     /**
29806      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29807      */
29808     /**
29809      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29810      */
29811     /**
29812      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29813      */
29814     buttonAlign:'center',
29815
29816     /**
29817      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29818      */
29819     minButtonWidth:75,
29820
29821     /**
29822      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29823      * This property cascades to child containers if not set.
29824      */
29825     labelAlign:'left',
29826
29827     /**
29828      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29829      * fires a looping event with that state. This is required to bind buttons to the valid
29830      * state using the config value formBind:true on the button.
29831      */
29832     monitorValid : false,
29833
29834     /**
29835      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29836      */
29837     monitorPoll : 200,
29838     
29839     /**
29840      * @cfg {String} progressUrl - Url to return progress data 
29841      */
29842     
29843     progressUrl : false,
29844     /**
29845      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
29846      * sending a formdata with extra parameters - eg uploaded elements.
29847      */
29848     
29849     formData : false,
29850     
29851     /**
29852      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29853      * fields are added and the column is closed. If no fields are passed the column remains open
29854      * until end() is called.
29855      * @param {Object} config The config to pass to the column
29856      * @param {Field} field1 (optional)
29857      * @param {Field} field2 (optional)
29858      * @param {Field} etc (optional)
29859      * @return Column The column container object
29860      */
29861     column : function(c){
29862         var col = new Roo.form.Column(c);
29863         this.start(col);
29864         if(arguments.length > 1){ // duplicate code required because of Opera
29865             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29866             this.end();
29867         }
29868         return col;
29869     },
29870
29871     /**
29872      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29873      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29874      * until end() is called.
29875      * @param {Object} config The config to pass to the fieldset
29876      * @param {Field} field1 (optional)
29877      * @param {Field} field2 (optional)
29878      * @param {Field} etc (optional)
29879      * @return FieldSet The fieldset container object
29880      */
29881     fieldset : function(c){
29882         var fs = new Roo.form.FieldSet(c);
29883         this.start(fs);
29884         if(arguments.length > 1){ // duplicate code required because of Opera
29885             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29886             this.end();
29887         }
29888         return fs;
29889     },
29890
29891     /**
29892      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29893      * fields are added and the container is closed. If no fields are passed the container remains open
29894      * until end() is called.
29895      * @param {Object} config The config to pass to the Layout
29896      * @param {Field} field1 (optional)
29897      * @param {Field} field2 (optional)
29898      * @param {Field} etc (optional)
29899      * @return Layout The container object
29900      */
29901     container : function(c){
29902         var l = new Roo.form.Layout(c);
29903         this.start(l);
29904         if(arguments.length > 1){ // duplicate code required because of Opera
29905             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29906             this.end();
29907         }
29908         return l;
29909     },
29910
29911     /**
29912      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29913      * @param {Object} container A Roo.form.Layout or subclass of Layout
29914      * @return {Form} this
29915      */
29916     start : function(c){
29917         // cascade label info
29918         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29919         this.active.stack.push(c);
29920         c.ownerCt = this.active;
29921         this.active = c;
29922         return this;
29923     },
29924
29925     /**
29926      * Closes the current open container
29927      * @return {Form} this
29928      */
29929     end : function(){
29930         if(this.active == this.root){
29931             return this;
29932         }
29933         this.active = this.active.ownerCt;
29934         return this;
29935     },
29936
29937     /**
29938      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29939      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29940      * as the label of the field.
29941      * @param {Field} field1
29942      * @param {Field} field2 (optional)
29943      * @param {Field} etc. (optional)
29944      * @return {Form} this
29945      */
29946     add : function(){
29947         this.active.stack.push.apply(this.active.stack, arguments);
29948         this.allItems.push.apply(this.allItems,arguments);
29949         var r = [];
29950         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29951             if(a[i].isFormField){
29952                 r.push(a[i]);
29953             }
29954         }
29955         if(r.length > 0){
29956             Roo.form.Form.superclass.add.apply(this, r);
29957         }
29958         return this;
29959     },
29960     
29961
29962     
29963     
29964     
29965      /**
29966      * Find any element that has been added to a form, using it's ID or name
29967      * This can include framesets, columns etc. along with regular fields..
29968      * @param {String} id - id or name to find.
29969      
29970      * @return {Element} e - or false if nothing found.
29971      */
29972     findbyId : function(id)
29973     {
29974         var ret = false;
29975         if (!id) {
29976             return ret;
29977         }
29978         Roo.each(this.allItems, function(f){
29979             if (f.id == id || f.name == id ){
29980                 ret = f;
29981                 return false;
29982             }
29983         });
29984         return ret;
29985     },
29986
29987     
29988     
29989     /**
29990      * Render this form into the passed container. This should only be called once!
29991      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29992      * @return {Form} this
29993      */
29994     render : function(ct)
29995     {
29996         
29997         
29998         
29999         ct = Roo.get(ct);
30000         var o = this.autoCreate || {
30001             tag: 'form',
30002             method : this.method || 'POST',
30003             id : this.id || Roo.id()
30004         };
30005         this.initEl(ct.createChild(o));
30006
30007         this.root.render(this.el);
30008         
30009        
30010              
30011         this.items.each(function(f){
30012             f.render('x-form-el-'+f.id);
30013         });
30014
30015         if(this.buttons.length > 0){
30016             // tables are required to maintain order and for correct IE layout
30017             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30018                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30019                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30020             }}, null, true);
30021             var tr = tb.getElementsByTagName('tr')[0];
30022             for(var i = 0, len = this.buttons.length; i < len; i++) {
30023                 var b = this.buttons[i];
30024                 var td = document.createElement('td');
30025                 td.className = 'x-form-btn-td';
30026                 b.render(tr.appendChild(td));
30027             }
30028         }
30029         if(this.monitorValid){ // initialize after render
30030             this.startMonitoring();
30031         }
30032         this.fireEvent('rendered', this);
30033         return this;
30034     },
30035
30036     /**
30037      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30038      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30039      * object or a valid Roo.DomHelper element config
30040      * @param {Function} handler The function called when the button is clicked
30041      * @param {Object} scope (optional) The scope of the handler function
30042      * @return {Roo.Button}
30043      */
30044     addButton : function(config, handler, scope){
30045         var bc = {
30046             handler: handler,
30047             scope: scope,
30048             minWidth: this.minButtonWidth,
30049             hideParent:true
30050         };
30051         if(typeof config == "string"){
30052             bc.text = config;
30053         }else{
30054             Roo.apply(bc, config);
30055         }
30056         var btn = new Roo.Button(null, bc);
30057         this.buttons.push(btn);
30058         return btn;
30059     },
30060
30061      /**
30062      * Adds a series of form elements (using the xtype property as the factory method.
30063      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30064      * @param {Object} config 
30065      */
30066     
30067     addxtype : function()
30068     {
30069         var ar = Array.prototype.slice.call(arguments, 0);
30070         var ret = false;
30071         for(var i = 0; i < ar.length; i++) {
30072             if (!ar[i]) {
30073                 continue; // skip -- if this happends something invalid got sent, we 
30074                 // should ignore it, as basically that interface element will not show up
30075                 // and that should be pretty obvious!!
30076             }
30077             
30078             if (Roo.form[ar[i].xtype]) {
30079                 ar[i].form = this;
30080                 var fe = Roo.factory(ar[i], Roo.form);
30081                 if (!ret) {
30082                     ret = fe;
30083                 }
30084                 fe.form = this;
30085                 if (fe.store) {
30086                     fe.store.form = this;
30087                 }
30088                 if (fe.isLayout) {  
30089                          
30090                     this.start(fe);
30091                     this.allItems.push(fe);
30092                     if (fe.items && fe.addxtype) {
30093                         fe.addxtype.apply(fe, fe.items);
30094                         delete fe.items;
30095                     }
30096                      this.end();
30097                     continue;
30098                 }
30099                 
30100                 
30101                  
30102                 this.add(fe);
30103               //  console.log('adding ' + ar[i].xtype);
30104             }
30105             if (ar[i].xtype == 'Button') {  
30106                 //console.log('adding button');
30107                 //console.log(ar[i]);
30108                 this.addButton(ar[i]);
30109                 this.allItems.push(fe);
30110                 continue;
30111             }
30112             
30113             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30114                 alert('end is not supported on xtype any more, use items');
30115             //    this.end();
30116             //    //console.log('adding end');
30117             }
30118             
30119         }
30120         return ret;
30121     },
30122     
30123     /**
30124      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30125      * option "monitorValid"
30126      */
30127     startMonitoring : function(){
30128         if(!this.bound){
30129             this.bound = true;
30130             Roo.TaskMgr.start({
30131                 run : this.bindHandler,
30132                 interval : this.monitorPoll || 200,
30133                 scope: this
30134             });
30135         }
30136     },
30137
30138     /**
30139      * Stops monitoring of the valid state of this form
30140      */
30141     stopMonitoring : function(){
30142         this.bound = false;
30143     },
30144
30145     // private
30146     bindHandler : function(){
30147         if(!this.bound){
30148             return false; // stops binding
30149         }
30150         var valid = true;
30151         this.items.each(function(f){
30152             if(!f.isValid(true)){
30153                 valid = false;
30154                 return false;
30155             }
30156         });
30157         for(var i = 0, len = this.buttons.length; i < len; i++){
30158             var btn = this.buttons[i];
30159             if(btn.formBind === true && btn.disabled === valid){
30160                 btn.setDisabled(!valid);
30161             }
30162         }
30163         this.fireEvent('clientvalidation', this, valid);
30164     }
30165     
30166     
30167     
30168     
30169     
30170     
30171     
30172     
30173 });
30174
30175
30176 // back compat
30177 Roo.Form = Roo.form.Form;
30178 /*
30179  * Based on:
30180  * Ext JS Library 1.1.1
30181  * Copyright(c) 2006-2007, Ext JS, LLC.
30182  *
30183  * Originally Released Under LGPL - original licence link has changed is not relivant.
30184  *
30185  * Fork - LGPL
30186  * <script type="text/javascript">
30187  */
30188
30189 // as we use this in bootstrap.
30190 Roo.namespace('Roo.form');
30191  /**
30192  * @class Roo.form.Action
30193  * Internal Class used to handle form actions
30194  * @constructor
30195  * @param {Roo.form.BasicForm} el The form element or its id
30196  * @param {Object} config Configuration options
30197  */
30198
30199  
30200  
30201 // define the action interface
30202 Roo.form.Action = function(form, options){
30203     this.form = form;
30204     this.options = options || {};
30205 };
30206 /**
30207  * Client Validation Failed
30208  * @const 
30209  */
30210 Roo.form.Action.CLIENT_INVALID = 'client';
30211 /**
30212  * Server Validation Failed
30213  * @const 
30214  */
30215 Roo.form.Action.SERVER_INVALID = 'server';
30216  /**
30217  * Connect to Server Failed
30218  * @const 
30219  */
30220 Roo.form.Action.CONNECT_FAILURE = 'connect';
30221 /**
30222  * Reading Data from Server Failed
30223  * @const 
30224  */
30225 Roo.form.Action.LOAD_FAILURE = 'load';
30226
30227 Roo.form.Action.prototype = {
30228     type : 'default',
30229     failureType : undefined,
30230     response : undefined,
30231     result : undefined,
30232
30233     // interface method
30234     run : function(options){
30235
30236     },
30237
30238     // interface method
30239     success : function(response){
30240
30241     },
30242
30243     // interface method
30244     handleResponse : function(response){
30245
30246     },
30247
30248     // default connection failure
30249     failure : function(response){
30250         
30251         this.response = response;
30252         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30253         this.form.afterAction(this, false);
30254     },
30255
30256     processResponse : function(response){
30257         this.response = response;
30258         if(!response.responseText){
30259             return true;
30260         }
30261         this.result = this.handleResponse(response);
30262         return this.result;
30263     },
30264
30265     // utility functions used internally
30266     getUrl : function(appendParams){
30267         var url = this.options.url || this.form.url || this.form.el.dom.action;
30268         if(appendParams){
30269             var p = this.getParams();
30270             if(p){
30271                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30272             }
30273         }
30274         return url;
30275     },
30276
30277     getMethod : function(){
30278         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30279     },
30280
30281     getParams : function(){
30282         var bp = this.form.baseParams;
30283         var p = this.options.params;
30284         if(p){
30285             if(typeof p == "object"){
30286                 p = Roo.urlEncode(Roo.applyIf(p, bp));
30287             }else if(typeof p == 'string' && bp){
30288                 p += '&' + Roo.urlEncode(bp);
30289             }
30290         }else if(bp){
30291             p = Roo.urlEncode(bp);
30292         }
30293         return p;
30294     },
30295
30296     createCallback : function(){
30297         return {
30298             success: this.success,
30299             failure: this.failure,
30300             scope: this,
30301             timeout: (this.form.timeout*1000),
30302             upload: this.form.fileUpload ? this.success : undefined
30303         };
30304     }
30305 };
30306
30307 Roo.form.Action.Submit = function(form, options){
30308     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30309 };
30310
30311 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30312     type : 'submit',
30313
30314     haveProgress : false,
30315     uploadComplete : false,
30316     
30317     // uploadProgress indicator.
30318     uploadProgress : function()
30319     {
30320         if (!this.form.progressUrl) {
30321             return;
30322         }
30323         
30324         if (!this.haveProgress) {
30325             Roo.MessageBox.progress("Uploading", "Uploading");
30326         }
30327         if (this.uploadComplete) {
30328            Roo.MessageBox.hide();
30329            return;
30330         }
30331         
30332         this.haveProgress = true;
30333    
30334         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30335         
30336         var c = new Roo.data.Connection();
30337         c.request({
30338             url : this.form.progressUrl,
30339             params: {
30340                 id : uid
30341             },
30342             method: 'GET',
30343             success : function(req){
30344                //console.log(data);
30345                 var rdata = false;
30346                 var edata;
30347                 try  {
30348                    rdata = Roo.decode(req.responseText)
30349                 } catch (e) {
30350                     Roo.log("Invalid data from server..");
30351                     Roo.log(edata);
30352                     return;
30353                 }
30354                 if (!rdata || !rdata.success) {
30355                     Roo.log(rdata);
30356                     Roo.MessageBox.alert(Roo.encode(rdata));
30357                     return;
30358                 }
30359                 var data = rdata.data;
30360                 
30361                 if (this.uploadComplete) {
30362                    Roo.MessageBox.hide();
30363                    return;
30364                 }
30365                    
30366                 if (data){
30367                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30368                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30369                     );
30370                 }
30371                 this.uploadProgress.defer(2000,this);
30372             },
30373        
30374             failure: function(data) {
30375                 Roo.log('progress url failed ');
30376                 Roo.log(data);
30377             },
30378             scope : this
30379         });
30380            
30381     },
30382     
30383     
30384     run : function()
30385     {
30386         // run get Values on the form, so it syncs any secondary forms.
30387         this.form.getValues();
30388         
30389         var o = this.options;
30390         var method = this.getMethod();
30391         var isPost = method == 'POST';
30392         if(o.clientValidation === false || this.form.isValid()){
30393             
30394             if (this.form.progressUrl) {
30395                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30396                     (new Date() * 1) + '' + Math.random());
30397                     
30398             } 
30399             
30400             
30401             Roo.Ajax.request(Roo.apply(this.createCallback(), {
30402                 form:this.form.el.dom,
30403                 url:this.getUrl(!isPost),
30404                 method: method,
30405                 params:isPost ? this.getParams() : null,
30406                 isUpload: this.form.fileUpload,
30407                 formData : this.form.formData
30408             }));
30409             
30410             this.uploadProgress();
30411
30412         }else if (o.clientValidation !== false){ // client validation failed
30413             this.failureType = Roo.form.Action.CLIENT_INVALID;
30414             this.form.afterAction(this, false);
30415         }
30416     },
30417
30418     success : function(response)
30419     {
30420         this.uploadComplete= true;
30421         if (this.haveProgress) {
30422             Roo.MessageBox.hide();
30423         }
30424         
30425         
30426         var result = this.processResponse(response);
30427         if(result === true || result.success){
30428             this.form.afterAction(this, true);
30429             return;
30430         }
30431         if(result.errors){
30432             this.form.markInvalid(result.errors);
30433             this.failureType = Roo.form.Action.SERVER_INVALID;
30434         }
30435         this.form.afterAction(this, false);
30436     },
30437     failure : function(response)
30438     {
30439         this.uploadComplete= true;
30440         if (this.haveProgress) {
30441             Roo.MessageBox.hide();
30442         }
30443         
30444         this.response = response;
30445         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30446         this.form.afterAction(this, false);
30447     },
30448     
30449     handleResponse : function(response){
30450         if(this.form.errorReader){
30451             var rs = this.form.errorReader.read(response);
30452             var errors = [];
30453             if(rs.records){
30454                 for(var i = 0, len = rs.records.length; i < len; i++) {
30455                     var r = rs.records[i];
30456                     errors[i] = r.data;
30457                 }
30458             }
30459             if(errors.length < 1){
30460                 errors = null;
30461             }
30462             return {
30463                 success : rs.success,
30464                 errors : errors
30465             };
30466         }
30467         var ret = false;
30468         try {
30469             ret = Roo.decode(response.responseText);
30470         } catch (e) {
30471             ret = {
30472                 success: false,
30473                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30474                 errors : []
30475             };
30476         }
30477         return ret;
30478         
30479     }
30480 });
30481
30482
30483 Roo.form.Action.Load = function(form, options){
30484     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30485     this.reader = this.form.reader;
30486 };
30487
30488 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30489     type : 'load',
30490
30491     run : function(){
30492         
30493         Roo.Ajax.request(Roo.apply(
30494                 this.createCallback(), {
30495                     method:this.getMethod(),
30496                     url:this.getUrl(false),
30497                     params:this.getParams()
30498         }));
30499     },
30500
30501     success : function(response){
30502         
30503         var result = this.processResponse(response);
30504         if(result === true || !result.success || !result.data){
30505             this.failureType = Roo.form.Action.LOAD_FAILURE;
30506             this.form.afterAction(this, false);
30507             return;
30508         }
30509         this.form.clearInvalid();
30510         this.form.setValues(result.data);
30511         this.form.afterAction(this, true);
30512     },
30513
30514     handleResponse : function(response){
30515         if(this.form.reader){
30516             var rs = this.form.reader.read(response);
30517             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30518             return {
30519                 success : rs.success,
30520                 data : data
30521             };
30522         }
30523         return Roo.decode(response.responseText);
30524     }
30525 });
30526
30527 Roo.form.Action.ACTION_TYPES = {
30528     'load' : Roo.form.Action.Load,
30529     'submit' : Roo.form.Action.Submit
30530 };/*
30531  * Based on:
30532  * Ext JS Library 1.1.1
30533  * Copyright(c) 2006-2007, Ext JS, LLC.
30534  *
30535  * Originally Released Under LGPL - original licence link has changed is not relivant.
30536  *
30537  * Fork - LGPL
30538  * <script type="text/javascript">
30539  */
30540  
30541 /**
30542  * @class Roo.form.Layout
30543  * @extends Roo.Component
30544  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30545  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30546  * @constructor
30547  * @param {Object} config Configuration options
30548  */
30549 Roo.form.Layout = function(config){
30550     var xitems = [];
30551     if (config.items) {
30552         xitems = config.items;
30553         delete config.items;
30554     }
30555     Roo.form.Layout.superclass.constructor.call(this, config);
30556     this.stack = [];
30557     Roo.each(xitems, this.addxtype, this);
30558      
30559 };
30560
30561 Roo.extend(Roo.form.Layout, Roo.Component, {
30562     /**
30563      * @cfg {String/Object} autoCreate
30564      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30565      */
30566     /**
30567      * @cfg {String/Object/Function} style
30568      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30569      * a function which returns such a specification.
30570      */
30571     /**
30572      * @cfg {String} labelAlign
30573      * Valid values are "left," "top" and "right" (defaults to "left")
30574      */
30575     /**
30576      * @cfg {Number} labelWidth
30577      * Fixed width in pixels of all field labels (defaults to undefined)
30578      */
30579     /**
30580      * @cfg {Boolean} clear
30581      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30582      */
30583     clear : true,
30584     /**
30585      * @cfg {String} labelSeparator
30586      * The separator to use after field labels (defaults to ':')
30587      */
30588     labelSeparator : ':',
30589     /**
30590      * @cfg {Boolean} hideLabels
30591      * True to suppress the display of field labels in this layout (defaults to false)
30592      */
30593     hideLabels : false,
30594
30595     // private
30596     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30597     
30598     isLayout : true,
30599     
30600     // private
30601     onRender : function(ct, position){
30602         if(this.el){ // from markup
30603             this.el = Roo.get(this.el);
30604         }else {  // generate
30605             var cfg = this.getAutoCreate();
30606             this.el = ct.createChild(cfg, position);
30607         }
30608         if(this.style){
30609             this.el.applyStyles(this.style);
30610         }
30611         if(this.labelAlign){
30612             this.el.addClass('x-form-label-'+this.labelAlign);
30613         }
30614         if(this.hideLabels){
30615             this.labelStyle = "display:none";
30616             this.elementStyle = "padding-left:0;";
30617         }else{
30618             if(typeof this.labelWidth == 'number'){
30619                 this.labelStyle = "width:"+this.labelWidth+"px;";
30620                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30621             }
30622             if(this.labelAlign == 'top'){
30623                 this.labelStyle = "width:auto;";
30624                 this.elementStyle = "padding-left:0;";
30625             }
30626         }
30627         var stack = this.stack;
30628         var slen = stack.length;
30629         if(slen > 0){
30630             if(!this.fieldTpl){
30631                 var t = new Roo.Template(
30632                     '<div class="x-form-item {5}">',
30633                         '<label for="{0}" style="{2}">{1}{4}</label>',
30634                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30635                         '</div>',
30636                     '</div><div class="x-form-clear-left"></div>'
30637                 );
30638                 t.disableFormats = true;
30639                 t.compile();
30640                 Roo.form.Layout.prototype.fieldTpl = t;
30641             }
30642             for(var i = 0; i < slen; i++) {
30643                 if(stack[i].isFormField){
30644                     this.renderField(stack[i]);
30645                 }else{
30646                     this.renderComponent(stack[i]);
30647                 }
30648             }
30649         }
30650         if(this.clear){
30651             this.el.createChild({cls:'x-form-clear'});
30652         }
30653     },
30654
30655     // private
30656     renderField : function(f){
30657         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30658                f.id, //0
30659                f.fieldLabel, //1
30660                f.labelStyle||this.labelStyle||'', //2
30661                this.elementStyle||'', //3
30662                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30663                f.itemCls||this.itemCls||''  //5
30664        ], true).getPrevSibling());
30665     },
30666
30667     // private
30668     renderComponent : function(c){
30669         c.render(c.isLayout ? this.el : this.el.createChild());    
30670     },
30671     /**
30672      * Adds a object form elements (using the xtype property as the factory method.)
30673      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30674      * @param {Object} config 
30675      */
30676     addxtype : function(o)
30677     {
30678         // create the lement.
30679         o.form = this.form;
30680         var fe = Roo.factory(o, Roo.form);
30681         this.form.allItems.push(fe);
30682         this.stack.push(fe);
30683         
30684         if (fe.isFormField) {
30685             this.form.items.add(fe);
30686         }
30687          
30688         return fe;
30689     }
30690 });
30691
30692 /**
30693  * @class Roo.form.Column
30694  * @extends Roo.form.Layout
30695  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30696  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30697  * @constructor
30698  * @param {Object} config Configuration options
30699  */
30700 Roo.form.Column = function(config){
30701     Roo.form.Column.superclass.constructor.call(this, config);
30702 };
30703
30704 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30705     /**
30706      * @cfg {Number/String} width
30707      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30708      */
30709     /**
30710      * @cfg {String/Object} autoCreate
30711      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30712      */
30713
30714     // private
30715     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30716
30717     // private
30718     onRender : function(ct, position){
30719         Roo.form.Column.superclass.onRender.call(this, ct, position);
30720         if(this.width){
30721             this.el.setWidth(this.width);
30722         }
30723     }
30724 });
30725
30726
30727 /**
30728  * @class Roo.form.Row
30729  * @extends Roo.form.Layout
30730  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30731  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30732  * @constructor
30733  * @param {Object} config Configuration options
30734  */
30735
30736  
30737 Roo.form.Row = function(config){
30738     Roo.form.Row.superclass.constructor.call(this, config);
30739 };
30740  
30741 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30742       /**
30743      * @cfg {Number/String} width
30744      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30745      */
30746     /**
30747      * @cfg {Number/String} height
30748      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30749      */
30750     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30751     
30752     padWidth : 20,
30753     // private
30754     onRender : function(ct, position){
30755         //console.log('row render');
30756         if(!this.rowTpl){
30757             var t = new Roo.Template(
30758                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30759                     '<label for="{0}" style="{2}">{1}{4}</label>',
30760                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30761                     '</div>',
30762                 '</div>'
30763             );
30764             t.disableFormats = true;
30765             t.compile();
30766             Roo.form.Layout.prototype.rowTpl = t;
30767         }
30768         this.fieldTpl = this.rowTpl;
30769         
30770         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30771         var labelWidth = 100;
30772         
30773         if ((this.labelAlign != 'top')) {
30774             if (typeof this.labelWidth == 'number') {
30775                 labelWidth = this.labelWidth
30776             }
30777             this.padWidth =  20 + labelWidth;
30778             
30779         }
30780         
30781         Roo.form.Column.superclass.onRender.call(this, ct, position);
30782         if(this.width){
30783             this.el.setWidth(this.width);
30784         }
30785         if(this.height){
30786             this.el.setHeight(this.height);
30787         }
30788     },
30789     
30790     // private
30791     renderField : function(f){
30792         f.fieldEl = this.fieldTpl.append(this.el, [
30793                f.id, f.fieldLabel,
30794                f.labelStyle||this.labelStyle||'',
30795                this.elementStyle||'',
30796                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30797                f.itemCls||this.itemCls||'',
30798                f.width ? f.width + this.padWidth : 160 + this.padWidth
30799        ],true);
30800     }
30801 });
30802  
30803
30804 /**
30805  * @class Roo.form.FieldSet
30806  * @extends Roo.form.Layout
30807  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
30808  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30809  * @constructor
30810  * @param {Object} config Configuration options
30811  */
30812 Roo.form.FieldSet = function(config){
30813     Roo.form.FieldSet.superclass.constructor.call(this, config);
30814 };
30815
30816 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30817     /**
30818      * @cfg {String} legend
30819      * The text to display as the legend for the FieldSet (defaults to '')
30820      */
30821     /**
30822      * @cfg {String/Object} autoCreate
30823      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30824      */
30825
30826     // private
30827     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30828
30829     // private
30830     onRender : function(ct, position){
30831         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30832         if(this.legend){
30833             this.setLegend(this.legend);
30834         }
30835     },
30836
30837     // private
30838     setLegend : function(text){
30839         if(this.rendered){
30840             this.el.child('legend').update(text);
30841         }
30842     }
30843 });/*
30844  * Based on:
30845  * Ext JS Library 1.1.1
30846  * Copyright(c) 2006-2007, Ext JS, LLC.
30847  *
30848  * Originally Released Under LGPL - original licence link has changed is not relivant.
30849  *
30850  * Fork - LGPL
30851  * <script type="text/javascript">
30852  */
30853 /**
30854  * @class Roo.form.VTypes
30855  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30856  * @static
30857  */
30858 Roo.form.VTypes = function(){
30859     // closure these in so they are only created once.
30860     var alpha = /^[a-zA-Z_]+$/;
30861     var alphanum = /^[a-zA-Z0-9_]+$/;
30862     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30863     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30864
30865     // All these messages and functions are configurable
30866     return {
30867         /**
30868          * The function used to validate email addresses
30869          * @param {String} value The email address
30870          */
30871         'email' : function(v){
30872             return email.test(v);
30873         },
30874         /**
30875          * The error text to display when the email validation function returns false
30876          * @type String
30877          */
30878         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30879         /**
30880          * The keystroke filter mask to be applied on email input
30881          * @type RegExp
30882          */
30883         'emailMask' : /[a-z0-9_\.\-@]/i,
30884
30885         /**
30886          * The function used to validate URLs
30887          * @param {String} value The URL
30888          */
30889         'url' : function(v){
30890             return url.test(v);
30891         },
30892         /**
30893          * The error text to display when the url validation function returns false
30894          * @type String
30895          */
30896         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30897         
30898         /**
30899          * The function used to validate alpha values
30900          * @param {String} value The value
30901          */
30902         'alpha' : function(v){
30903             return alpha.test(v);
30904         },
30905         /**
30906          * The error text to display when the alpha validation function returns false
30907          * @type String
30908          */
30909         'alphaText' : 'This field should only contain letters and _',
30910         /**
30911          * The keystroke filter mask to be applied on alpha input
30912          * @type RegExp
30913          */
30914         'alphaMask' : /[a-z_]/i,
30915
30916         /**
30917          * The function used to validate alphanumeric values
30918          * @param {String} value The value
30919          */
30920         'alphanum' : function(v){
30921             return alphanum.test(v);
30922         },
30923         /**
30924          * The error text to display when the alphanumeric validation function returns false
30925          * @type String
30926          */
30927         'alphanumText' : 'This field should only contain letters, numbers and _',
30928         /**
30929          * The keystroke filter mask to be applied on alphanumeric input
30930          * @type RegExp
30931          */
30932         'alphanumMask' : /[a-z0-9_]/i
30933     };
30934 }();//<script type="text/javascript">
30935
30936 /**
30937  * @class Roo.form.FCKeditor
30938  * @extends Roo.form.TextArea
30939  * Wrapper around the FCKEditor http://www.fckeditor.net
30940  * @constructor
30941  * Creates a new FCKeditor
30942  * @param {Object} config Configuration options
30943  */
30944 Roo.form.FCKeditor = function(config){
30945     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30946     this.addEvents({
30947          /**
30948          * @event editorinit
30949          * Fired when the editor is initialized - you can add extra handlers here..
30950          * @param {FCKeditor} this
30951          * @param {Object} the FCK object.
30952          */
30953         editorinit : true
30954     });
30955     
30956     
30957 };
30958 Roo.form.FCKeditor.editors = { };
30959 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30960 {
30961     //defaultAutoCreate : {
30962     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30963     //},
30964     // private
30965     /**
30966      * @cfg {Object} fck options - see fck manual for details.
30967      */
30968     fckconfig : false,
30969     
30970     /**
30971      * @cfg {Object} fck toolbar set (Basic or Default)
30972      */
30973     toolbarSet : 'Basic',
30974     /**
30975      * @cfg {Object} fck BasePath
30976      */ 
30977     basePath : '/fckeditor/',
30978     
30979     
30980     frame : false,
30981     
30982     value : '',
30983     
30984    
30985     onRender : function(ct, position)
30986     {
30987         if(!this.el){
30988             this.defaultAutoCreate = {
30989                 tag: "textarea",
30990                 style:"width:300px;height:60px;",
30991                 autocomplete: "new-password"
30992             };
30993         }
30994         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30995         /*
30996         if(this.grow){
30997             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30998             if(this.preventScrollbars){
30999                 this.el.setStyle("overflow", "hidden");
31000             }
31001             this.el.setHeight(this.growMin);
31002         }
31003         */
31004         //console.log('onrender' + this.getId() );
31005         Roo.form.FCKeditor.editors[this.getId()] = this;
31006          
31007
31008         this.replaceTextarea() ;
31009         
31010     },
31011     
31012     getEditor : function() {
31013         return this.fckEditor;
31014     },
31015     /**
31016      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
31017      * @param {Mixed} value The value to set
31018      */
31019     
31020     
31021     setValue : function(value)
31022     {
31023         //console.log('setValue: ' + value);
31024         
31025         if(typeof(value) == 'undefined') { // not sure why this is happending...
31026             return;
31027         }
31028         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31029         
31030         //if(!this.el || !this.getEditor()) {
31031         //    this.value = value;
31032             //this.setValue.defer(100,this,[value]);    
31033         //    return;
31034         //} 
31035         
31036         if(!this.getEditor()) {
31037             return;
31038         }
31039         
31040         this.getEditor().SetData(value);
31041         
31042         //
31043
31044     },
31045
31046     /**
31047      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
31048      * @return {Mixed} value The field value
31049      */
31050     getValue : function()
31051     {
31052         
31053         if (this.frame && this.frame.dom.style.display == 'none') {
31054             return Roo.form.FCKeditor.superclass.getValue.call(this);
31055         }
31056         
31057         if(!this.el || !this.getEditor()) {
31058            
31059            // this.getValue.defer(100,this); 
31060             return this.value;
31061         }
31062        
31063         
31064         var value=this.getEditor().GetData();
31065         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31066         return Roo.form.FCKeditor.superclass.getValue.call(this);
31067         
31068
31069     },
31070
31071     /**
31072      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
31073      * @return {Mixed} value The field value
31074      */
31075     getRawValue : function()
31076     {
31077         if (this.frame && this.frame.dom.style.display == 'none') {
31078             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31079         }
31080         
31081         if(!this.el || !this.getEditor()) {
31082             //this.getRawValue.defer(100,this); 
31083             return this.value;
31084             return;
31085         }
31086         
31087         
31088         
31089         var value=this.getEditor().GetData();
31090         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31091         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31092          
31093     },
31094     
31095     setSize : function(w,h) {
31096         
31097         
31098         
31099         //if (this.frame && this.frame.dom.style.display == 'none') {
31100         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31101         //    return;
31102         //}
31103         //if(!this.el || !this.getEditor()) {
31104         //    this.setSize.defer(100,this, [w,h]); 
31105         //    return;
31106         //}
31107         
31108         
31109         
31110         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31111         
31112         this.frame.dom.setAttribute('width', w);
31113         this.frame.dom.setAttribute('height', h);
31114         this.frame.setSize(w,h);
31115         
31116     },
31117     
31118     toggleSourceEdit : function(value) {
31119         
31120       
31121          
31122         this.el.dom.style.display = value ? '' : 'none';
31123         this.frame.dom.style.display = value ?  'none' : '';
31124         
31125     },
31126     
31127     
31128     focus: function(tag)
31129     {
31130         if (this.frame.dom.style.display == 'none') {
31131             return Roo.form.FCKeditor.superclass.focus.call(this);
31132         }
31133         if(!this.el || !this.getEditor()) {
31134             this.focus.defer(100,this, [tag]); 
31135             return;
31136         }
31137         
31138         
31139         
31140         
31141         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31142         this.getEditor().Focus();
31143         if (tgs.length) {
31144             if (!this.getEditor().Selection.GetSelection()) {
31145                 this.focus.defer(100,this, [tag]); 
31146                 return;
31147             }
31148             
31149             
31150             var r = this.getEditor().EditorDocument.createRange();
31151             r.setStart(tgs[0],0);
31152             r.setEnd(tgs[0],0);
31153             this.getEditor().Selection.GetSelection().removeAllRanges();
31154             this.getEditor().Selection.GetSelection().addRange(r);
31155             this.getEditor().Focus();
31156         }
31157         
31158     },
31159     
31160     
31161     
31162     replaceTextarea : function()
31163     {
31164         if ( document.getElementById( this.getId() + '___Frame' ) ) {
31165             return ;
31166         }
31167         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31168         //{
31169             // We must check the elements firstly using the Id and then the name.
31170         var oTextarea = document.getElementById( this.getId() );
31171         
31172         var colElementsByName = document.getElementsByName( this.getId() ) ;
31173          
31174         oTextarea.style.display = 'none' ;
31175
31176         if ( oTextarea.tabIndex ) {            
31177             this.TabIndex = oTextarea.tabIndex ;
31178         }
31179         
31180         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31181         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31182         this.frame = Roo.get(this.getId() + '___Frame')
31183     },
31184     
31185     _getConfigHtml : function()
31186     {
31187         var sConfig = '' ;
31188
31189         for ( var o in this.fckconfig ) {
31190             sConfig += sConfig.length > 0  ? '&amp;' : '';
31191             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31192         }
31193
31194         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31195     },
31196     
31197     
31198     _getIFrameHtml : function()
31199     {
31200         var sFile = 'fckeditor.html' ;
31201         /* no idea what this is about..
31202         try
31203         {
31204             if ( (/fcksource=true/i).test( window.top.location.search ) )
31205                 sFile = 'fckeditor.original.html' ;
31206         }
31207         catch (e) { 
31208         */
31209
31210         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31211         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
31212         
31213         
31214         var html = '<iframe id="' + this.getId() +
31215             '___Frame" src="' + sLink +
31216             '" width="' + this.width +
31217             '" height="' + this.height + '"' +
31218             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
31219             ' frameborder="0" scrolling="no"></iframe>' ;
31220
31221         return html ;
31222     },
31223     
31224     _insertHtmlBefore : function( html, element )
31225     {
31226         if ( element.insertAdjacentHTML )       {
31227             // IE
31228             element.insertAdjacentHTML( 'beforeBegin', html ) ;
31229         } else { // Gecko
31230             var oRange = document.createRange() ;
31231             oRange.setStartBefore( element ) ;
31232             var oFragment = oRange.createContextualFragment( html );
31233             element.parentNode.insertBefore( oFragment, element ) ;
31234         }
31235     }
31236     
31237     
31238   
31239     
31240     
31241     
31242     
31243
31244 });
31245
31246 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31247
31248 function FCKeditor_OnComplete(editorInstance){
31249     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31250     f.fckEditor = editorInstance;
31251     //console.log("loaded");
31252     f.fireEvent('editorinit', f, editorInstance);
31253
31254   
31255
31256  
31257
31258
31259
31260
31261
31262
31263
31264
31265
31266
31267
31268
31269
31270
31271
31272 //<script type="text/javascript">
31273 /**
31274  * @class Roo.form.GridField
31275  * @extends Roo.form.Field
31276  * Embed a grid (or editable grid into a form)
31277  * STATUS ALPHA
31278  * 
31279  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31280  * it needs 
31281  * xgrid.store = Roo.data.Store
31282  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31283  * xgrid.store.reader = Roo.data.JsonReader 
31284  * 
31285  * 
31286  * @constructor
31287  * Creates a new GridField
31288  * @param {Object} config Configuration options
31289  */
31290 Roo.form.GridField = function(config){
31291     Roo.form.GridField.superclass.constructor.call(this, config);
31292      
31293 };
31294
31295 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
31296     /**
31297      * @cfg {Number} width  - used to restrict width of grid..
31298      */
31299     width : 100,
31300     /**
31301      * @cfg {Number} height - used to restrict height of grid..
31302      */
31303     height : 50,
31304      /**
31305      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31306          * 
31307          *}
31308      */
31309     xgrid : false, 
31310     /**
31311      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31312      * {tag: "input", type: "checkbox", autocomplete: "off"})
31313      */
31314    // defaultAutoCreate : { tag: 'div' },
31315     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31316     /**
31317      * @cfg {String} addTitle Text to include for adding a title.
31318      */
31319     addTitle : false,
31320     //
31321     onResize : function(){
31322         Roo.form.Field.superclass.onResize.apply(this, arguments);
31323     },
31324
31325     initEvents : function(){
31326         // Roo.form.Checkbox.superclass.initEvents.call(this);
31327         // has no events...
31328        
31329     },
31330
31331
31332     getResizeEl : function(){
31333         return this.wrap;
31334     },
31335
31336     getPositionEl : function(){
31337         return this.wrap;
31338     },
31339
31340     // private
31341     onRender : function(ct, position){
31342         
31343         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31344         var style = this.style;
31345         delete this.style;
31346         
31347         Roo.form.GridField.superclass.onRender.call(this, ct, position);
31348         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31349         this.viewEl = this.wrap.createChild({ tag: 'div' });
31350         if (style) {
31351             this.viewEl.applyStyles(style);
31352         }
31353         if (this.width) {
31354             this.viewEl.setWidth(this.width);
31355         }
31356         if (this.height) {
31357             this.viewEl.setHeight(this.height);
31358         }
31359         //if(this.inputValue !== undefined){
31360         //this.setValue(this.value);
31361         
31362         
31363         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31364         
31365         
31366         this.grid.render();
31367         this.grid.getDataSource().on('remove', this.refreshValue, this);
31368         this.grid.getDataSource().on('update', this.refreshValue, this);
31369         this.grid.on('afteredit', this.refreshValue, this);
31370  
31371     },
31372      
31373     
31374     /**
31375      * Sets the value of the item. 
31376      * @param {String} either an object  or a string..
31377      */
31378     setValue : function(v){
31379         //this.value = v;
31380         v = v || []; // empty set..
31381         // this does not seem smart - it really only affects memoryproxy grids..
31382         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31383             var ds = this.grid.getDataSource();
31384             // assumes a json reader..
31385             var data = {}
31386             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
31387             ds.loadData( data);
31388         }
31389         // clear selection so it does not get stale.
31390         if (this.grid.sm) { 
31391             this.grid.sm.clearSelections();
31392         }
31393         
31394         Roo.form.GridField.superclass.setValue.call(this, v);
31395         this.refreshValue();
31396         // should load data in the grid really....
31397     },
31398     
31399     // private
31400     refreshValue: function() {
31401          var val = [];
31402         this.grid.getDataSource().each(function(r) {
31403             val.push(r.data);
31404         });
31405         this.el.dom.value = Roo.encode(val);
31406     }
31407     
31408      
31409     
31410     
31411 });/*
31412  * Based on:
31413  * Ext JS Library 1.1.1
31414  * Copyright(c) 2006-2007, Ext JS, LLC.
31415  *
31416  * Originally Released Under LGPL - original licence link has changed is not relivant.
31417  *
31418  * Fork - LGPL
31419  * <script type="text/javascript">
31420  */
31421 /**
31422  * @class Roo.form.DisplayField
31423  * @extends Roo.form.Field
31424  * A generic Field to display non-editable data.
31425  * @cfg {Boolean} closable (true|false) default false
31426  * @constructor
31427  * Creates a new Display Field item.
31428  * @param {Object} config Configuration options
31429  */
31430 Roo.form.DisplayField = function(config){
31431     Roo.form.DisplayField.superclass.constructor.call(this, config);
31432     
31433     this.addEvents({
31434         /**
31435          * @event close
31436          * Fires after the click the close btn
31437              * @param {Roo.form.DisplayField} this
31438              */
31439         close : true
31440     });
31441 };
31442
31443 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
31444     inputType:      'hidden',
31445     allowBlank:     true,
31446     readOnly:         true,
31447     
31448  
31449     /**
31450      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31451      */
31452     focusClass : undefined,
31453     /**
31454      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31455      */
31456     fieldClass: 'x-form-field',
31457     
31458      /**
31459      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31460      */
31461     valueRenderer: undefined,
31462     
31463     width: 100,
31464     /**
31465      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31466      * {tag: "input", type: "checkbox", autocomplete: "off"})
31467      */
31468      
31469  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31470  
31471     closable : false,
31472     
31473     onResize : function(){
31474         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31475         
31476     },
31477
31478     initEvents : function(){
31479         // Roo.form.Checkbox.superclass.initEvents.call(this);
31480         // has no events...
31481         
31482         if(this.closable){
31483             this.closeEl.on('click', this.onClose, this);
31484         }
31485        
31486     },
31487
31488
31489     getResizeEl : function(){
31490         return this.wrap;
31491     },
31492
31493     getPositionEl : function(){
31494         return this.wrap;
31495     },
31496
31497     // private
31498     onRender : function(ct, position){
31499         
31500         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31501         //if(this.inputValue !== undefined){
31502         this.wrap = this.el.wrap();
31503         
31504         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31505         
31506         if(this.closable){
31507             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31508         }
31509         
31510         if (this.bodyStyle) {
31511             this.viewEl.applyStyles(this.bodyStyle);
31512         }
31513         //this.viewEl.setStyle('padding', '2px');
31514         
31515         this.setValue(this.value);
31516         
31517     },
31518 /*
31519     // private
31520     initValue : Roo.emptyFn,
31521
31522   */
31523
31524         // private
31525     onClick : function(){
31526         
31527     },
31528
31529     /**
31530      * Sets the checked state of the checkbox.
31531      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31532      */
31533     setValue : function(v){
31534         this.value = v;
31535         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31536         // this might be called before we have a dom element..
31537         if (!this.viewEl) {
31538             return;
31539         }
31540         this.viewEl.dom.innerHTML = html;
31541         Roo.form.DisplayField.superclass.setValue.call(this, v);
31542
31543     },
31544     
31545     onClose : function(e)
31546     {
31547         e.preventDefault();
31548         
31549         this.fireEvent('close', this);
31550     }
31551 });/*
31552  * 
31553  * Licence- LGPL
31554  * 
31555  */
31556
31557 /**
31558  * @class Roo.form.DayPicker
31559  * @extends Roo.form.Field
31560  * A Day picker show [M] [T] [W] ....
31561  * @constructor
31562  * Creates a new Day Picker
31563  * @param {Object} config Configuration options
31564  */
31565 Roo.form.DayPicker= function(config){
31566     Roo.form.DayPicker.superclass.constructor.call(this, config);
31567      
31568 };
31569
31570 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31571     /**
31572      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31573      */
31574     focusClass : undefined,
31575     /**
31576      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31577      */
31578     fieldClass: "x-form-field",
31579    
31580     /**
31581      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31582      * {tag: "input", type: "checkbox", autocomplete: "off"})
31583      */
31584     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31585     
31586    
31587     actionMode : 'viewEl', 
31588     //
31589     // private
31590  
31591     inputType : 'hidden',
31592     
31593      
31594     inputElement: false, // real input element?
31595     basedOn: false, // ????
31596     
31597     isFormField: true, // not sure where this is needed!!!!
31598
31599     onResize : function(){
31600         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31601         if(!this.boxLabel){
31602             this.el.alignTo(this.wrap, 'c-c');
31603         }
31604     },
31605
31606     initEvents : function(){
31607         Roo.form.Checkbox.superclass.initEvents.call(this);
31608         this.el.on("click", this.onClick,  this);
31609         this.el.on("change", this.onClick,  this);
31610     },
31611
31612
31613     getResizeEl : function(){
31614         return this.wrap;
31615     },
31616
31617     getPositionEl : function(){
31618         return this.wrap;
31619     },
31620
31621     
31622     // private
31623     onRender : function(ct, position){
31624         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31625        
31626         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31627         
31628         var r1 = '<table><tr>';
31629         var r2 = '<tr class="x-form-daypick-icons">';
31630         for (var i=0; i < 7; i++) {
31631             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31632             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31633         }
31634         
31635         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31636         viewEl.select('img').on('click', this.onClick, this);
31637         this.viewEl = viewEl;   
31638         
31639         
31640         // this will not work on Chrome!!!
31641         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31642         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31643         
31644         
31645           
31646
31647     },
31648
31649     // private
31650     initValue : Roo.emptyFn,
31651
31652     /**
31653      * Returns the checked state of the checkbox.
31654      * @return {Boolean} True if checked, else false
31655      */
31656     getValue : function(){
31657         return this.el.dom.value;
31658         
31659     },
31660
31661         // private
31662     onClick : function(e){ 
31663         //this.setChecked(!this.checked);
31664         Roo.get(e.target).toggleClass('x-menu-item-checked');
31665         this.refreshValue();
31666         //if(this.el.dom.checked != this.checked){
31667         //    this.setValue(this.el.dom.checked);
31668        // }
31669     },
31670     
31671     // private
31672     refreshValue : function()
31673     {
31674         var val = '';
31675         this.viewEl.select('img',true).each(function(e,i,n)  {
31676             val += e.is(".x-menu-item-checked") ? String(n) : '';
31677         });
31678         this.setValue(val, true);
31679     },
31680
31681     /**
31682      * Sets the checked state of the checkbox.
31683      * On is always based on a string comparison between inputValue and the param.
31684      * @param {Boolean/String} value - the value to set 
31685      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31686      */
31687     setValue : function(v,suppressEvent){
31688         if (!this.el.dom) {
31689             return;
31690         }
31691         var old = this.el.dom.value ;
31692         this.el.dom.value = v;
31693         if (suppressEvent) {
31694             return ;
31695         }
31696          
31697         // update display..
31698         this.viewEl.select('img',true).each(function(e,i,n)  {
31699             
31700             var on = e.is(".x-menu-item-checked");
31701             var newv = v.indexOf(String(n)) > -1;
31702             if (on != newv) {
31703                 e.toggleClass('x-menu-item-checked');
31704             }
31705             
31706         });
31707         
31708         
31709         this.fireEvent('change', this, v, old);
31710         
31711         
31712     },
31713    
31714     // handle setting of hidden value by some other method!!?!?
31715     setFromHidden: function()
31716     {
31717         if(!this.el){
31718             return;
31719         }
31720         //console.log("SET FROM HIDDEN");
31721         //alert('setFrom hidden');
31722         this.setValue(this.el.dom.value);
31723     },
31724     
31725     onDestroy : function()
31726     {
31727         if(this.viewEl){
31728             Roo.get(this.viewEl).remove();
31729         }
31730          
31731         Roo.form.DayPicker.superclass.onDestroy.call(this);
31732     }
31733
31734 });/*
31735  * RooJS Library 1.1.1
31736  * Copyright(c) 2008-2011  Alan Knowles
31737  *
31738  * License - LGPL
31739  */
31740  
31741
31742 /**
31743  * @class Roo.form.ComboCheck
31744  * @extends Roo.form.ComboBox
31745  * A combobox for multiple select items.
31746  *
31747  * FIXME - could do with a reset button..
31748  * 
31749  * @constructor
31750  * Create a new ComboCheck
31751  * @param {Object} config Configuration options
31752  */
31753 Roo.form.ComboCheck = function(config){
31754     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31755     // should verify some data...
31756     // like
31757     // hiddenName = required..
31758     // displayField = required
31759     // valudField == required
31760     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31761     var _t = this;
31762     Roo.each(req, function(e) {
31763         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31764             throw "Roo.form.ComboCheck : missing value for: " + e;
31765         }
31766     });
31767     
31768     
31769 };
31770
31771 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31772      
31773      
31774     editable : false,
31775      
31776     selectedClass: 'x-menu-item-checked', 
31777     
31778     // private
31779     onRender : function(ct, position){
31780         var _t = this;
31781         
31782         
31783         
31784         if(!this.tpl){
31785             var cls = 'x-combo-list';
31786
31787             
31788             this.tpl =  new Roo.Template({
31789                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31790                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31791                    '<span>{' + this.displayField + '}</span>' +
31792                     '</div>' 
31793                 
31794             });
31795         }
31796  
31797         
31798         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31799         this.view.singleSelect = false;
31800         this.view.multiSelect = true;
31801         this.view.toggleSelect = true;
31802         this.pageTb.add(new Roo.Toolbar.Fill(), {
31803             
31804             text: 'Done',
31805             handler: function()
31806             {
31807                 _t.collapse();
31808             }
31809         });
31810     },
31811     
31812     onViewOver : function(e, t){
31813         // do nothing...
31814         return;
31815         
31816     },
31817     
31818     onViewClick : function(doFocus,index){
31819         return;
31820         
31821     },
31822     select: function () {
31823         //Roo.log("SELECT CALLED");
31824     },
31825      
31826     selectByValue : function(xv, scrollIntoView){
31827         var ar = this.getValueArray();
31828         var sels = [];
31829         
31830         Roo.each(ar, function(v) {
31831             if(v === undefined || v === null){
31832                 return;
31833             }
31834             var r = this.findRecord(this.valueField, v);
31835             if(r){
31836                 sels.push(this.store.indexOf(r))
31837                 
31838             }
31839         },this);
31840         this.view.select(sels);
31841         return false;
31842     },
31843     
31844     
31845     
31846     onSelect : function(record, index){
31847        // Roo.log("onselect Called");
31848        // this is only called by the clear button now..
31849         this.view.clearSelections();
31850         this.setValue('[]');
31851         if (this.value != this.valueBefore) {
31852             this.fireEvent('change', this, this.value, this.valueBefore);
31853             this.valueBefore = this.value;
31854         }
31855     },
31856     getValueArray : function()
31857     {
31858         var ar = [] ;
31859         
31860         try {
31861             //Roo.log(this.value);
31862             if (typeof(this.value) == 'undefined') {
31863                 return [];
31864             }
31865             var ar = Roo.decode(this.value);
31866             return  ar instanceof Array ? ar : []; //?? valid?
31867             
31868         } catch(e) {
31869             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31870             return [];
31871         }
31872          
31873     },
31874     expand : function ()
31875     {
31876         
31877         Roo.form.ComboCheck.superclass.expand.call(this);
31878         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31879         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31880         
31881
31882     },
31883     
31884     collapse : function(){
31885         Roo.form.ComboCheck.superclass.collapse.call(this);
31886         var sl = this.view.getSelectedIndexes();
31887         var st = this.store;
31888         var nv = [];
31889         var tv = [];
31890         var r;
31891         Roo.each(sl, function(i) {
31892             r = st.getAt(i);
31893             nv.push(r.get(this.valueField));
31894         },this);
31895         this.setValue(Roo.encode(nv));
31896         if (this.value != this.valueBefore) {
31897
31898             this.fireEvent('change', this, this.value, this.valueBefore);
31899             this.valueBefore = this.value;
31900         }
31901         
31902     },
31903     
31904     setValue : function(v){
31905         // Roo.log(v);
31906         this.value = v;
31907         
31908         var vals = this.getValueArray();
31909         var tv = [];
31910         Roo.each(vals, function(k) {
31911             var r = this.findRecord(this.valueField, k);
31912             if(r){
31913                 tv.push(r.data[this.displayField]);
31914             }else if(this.valueNotFoundText !== undefined){
31915                 tv.push( this.valueNotFoundText );
31916             }
31917         },this);
31918        // Roo.log(tv);
31919         
31920         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31921         this.hiddenField.value = v;
31922         this.value = v;
31923     }
31924     
31925 });/*
31926  * Based on:
31927  * Ext JS Library 1.1.1
31928  * Copyright(c) 2006-2007, Ext JS, LLC.
31929  *
31930  * Originally Released Under LGPL - original licence link has changed is not relivant.
31931  *
31932  * Fork - LGPL
31933  * <script type="text/javascript">
31934  */
31935  
31936 /**
31937  * @class Roo.form.Signature
31938  * @extends Roo.form.Field
31939  * Signature field.  
31940  * @constructor
31941  * 
31942  * @param {Object} config Configuration options
31943  */
31944
31945 Roo.form.Signature = function(config){
31946     Roo.form.Signature.superclass.constructor.call(this, config);
31947     
31948     this.addEvents({// not in used??
31949          /**
31950          * @event confirm
31951          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31952              * @param {Roo.form.Signature} combo This combo box
31953              */
31954         'confirm' : true,
31955         /**
31956          * @event reset
31957          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31958              * @param {Roo.form.ComboBox} combo This combo box
31959              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31960              */
31961         'reset' : true
31962     });
31963 };
31964
31965 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31966     /**
31967      * @cfg {Object} labels Label to use when rendering a form.
31968      * defaults to 
31969      * labels : { 
31970      *      clear : "Clear",
31971      *      confirm : "Confirm"
31972      *  }
31973      */
31974     labels : { 
31975         clear : "Clear",
31976         confirm : "Confirm"
31977     },
31978     /**
31979      * @cfg {Number} width The signature panel width (defaults to 300)
31980      */
31981     width: 300,
31982     /**
31983      * @cfg {Number} height The signature panel height (defaults to 100)
31984      */
31985     height : 100,
31986     /**
31987      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31988      */
31989     allowBlank : false,
31990     
31991     //private
31992     // {Object} signPanel The signature SVG panel element (defaults to {})
31993     signPanel : {},
31994     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31995     isMouseDown : false,
31996     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31997     isConfirmed : false,
31998     // {String} signatureTmp SVG mapping string (defaults to empty string)
31999     signatureTmp : '',
32000     
32001     
32002     defaultAutoCreate : { // modified by initCompnoent..
32003         tag: "input",
32004         type:"hidden"
32005     },
32006
32007     // private
32008     onRender : function(ct, position){
32009         
32010         Roo.form.Signature.superclass.onRender.call(this, ct, position);
32011         
32012         this.wrap = this.el.wrap({
32013             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32014         });
32015         
32016         this.createToolbar(this);
32017         this.signPanel = this.wrap.createChild({
32018                 tag: 'div',
32019                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32020             }, this.el
32021         );
32022             
32023         this.svgID = Roo.id();
32024         this.svgEl = this.signPanel.createChild({
32025               xmlns : 'http://www.w3.org/2000/svg',
32026               tag : 'svg',
32027               id : this.svgID + "-svg",
32028               width: this.width,
32029               height: this.height,
32030               viewBox: '0 0 '+this.width+' '+this.height,
32031               cn : [
32032                 {
32033                     tag: "rect",
32034                     id: this.svgID + "-svg-r",
32035                     width: this.width,
32036                     height: this.height,
32037                     fill: "#ffa"
32038                 },
32039                 {
32040                     tag: "line",
32041                     id: this.svgID + "-svg-l",
32042                     x1: "0", // start
32043                     y1: (this.height*0.8), // start set the line in 80% of height
32044                     x2: this.width, // end
32045                     y2: (this.height*0.8), // end set the line in 80% of height
32046                     'stroke': "#666",
32047                     'stroke-width': "1",
32048                     'stroke-dasharray': "3",
32049                     'shape-rendering': "crispEdges",
32050                     'pointer-events': "none"
32051                 },
32052                 {
32053                     tag: "path",
32054                     id: this.svgID + "-svg-p",
32055                     'stroke': "navy",
32056                     'stroke-width': "3",
32057                     'fill': "none",
32058                     'pointer-events': 'none'
32059                 }
32060               ]
32061         });
32062         this.createSVG();
32063         this.svgBox = this.svgEl.dom.getScreenCTM();
32064     },
32065     createSVG : function(){ 
32066         var svg = this.signPanel;
32067         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32068         var t = this;
32069
32070         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32071         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32072         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32073         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32074         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32075         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32076         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32077         
32078     },
32079     isTouchEvent : function(e){
32080         return e.type.match(/^touch/);
32081     },
32082     getCoords : function (e) {
32083         var pt    = this.svgEl.dom.createSVGPoint();
32084         pt.x = e.clientX; 
32085         pt.y = e.clientY;
32086         if (this.isTouchEvent(e)) {
32087             pt.x =  e.targetTouches[0].clientX;
32088             pt.y = e.targetTouches[0].clientY;
32089         }
32090         var a = this.svgEl.dom.getScreenCTM();
32091         var b = a.inverse();
32092         var mx = pt.matrixTransform(b);
32093         return mx.x + ',' + mx.y;
32094     },
32095     //mouse event headler 
32096     down : function (e) {
32097         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32098         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32099         
32100         this.isMouseDown = true;
32101         
32102         e.preventDefault();
32103     },
32104     move : function (e) {
32105         if (this.isMouseDown) {
32106             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32107             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32108         }
32109         
32110         e.preventDefault();
32111     },
32112     up : function (e) {
32113         this.isMouseDown = false;
32114         var sp = this.signatureTmp.split(' ');
32115         
32116         if(sp.length > 1){
32117             if(!sp[sp.length-2].match(/^L/)){
32118                 sp.pop();
32119                 sp.pop();
32120                 sp.push("");
32121                 this.signatureTmp = sp.join(" ");
32122             }
32123         }
32124         if(this.getValue() != this.signatureTmp){
32125             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32126             this.isConfirmed = false;
32127         }
32128         e.preventDefault();
32129     },
32130     
32131     /**
32132      * Protected method that will not generally be called directly. It
32133      * is called when the editor creates its toolbar. Override this method if you need to
32134      * add custom toolbar buttons.
32135      * @param {HtmlEditor} editor
32136      */
32137     createToolbar : function(editor){
32138          function btn(id, toggle, handler){
32139             var xid = fid + '-'+ id ;
32140             return {
32141                 id : xid,
32142                 cmd : id,
32143                 cls : 'x-btn-icon x-edit-'+id,
32144                 enableToggle:toggle !== false,
32145                 scope: editor, // was editor...
32146                 handler:handler||editor.relayBtnCmd,
32147                 clickEvent:'mousedown',
32148                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32149                 tabIndex:-1
32150             };
32151         }
32152         
32153         
32154         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32155         this.tb = tb;
32156         this.tb.add(
32157            {
32158                 cls : ' x-signature-btn x-signature-'+id,
32159                 scope: editor, // was editor...
32160                 handler: this.reset,
32161                 clickEvent:'mousedown',
32162                 text: this.labels.clear
32163             },
32164             {
32165                  xtype : 'Fill',
32166                  xns: Roo.Toolbar
32167             }, 
32168             {
32169                 cls : '  x-signature-btn x-signature-'+id,
32170                 scope: editor, // was editor...
32171                 handler: this.confirmHandler,
32172                 clickEvent:'mousedown',
32173                 text: this.labels.confirm
32174             }
32175         );
32176     
32177     },
32178     //public
32179     /**
32180      * when user is clicked confirm then show this image.....
32181      * 
32182      * @return {String} Image Data URI
32183      */
32184     getImageDataURI : function(){
32185         var svg = this.svgEl.dom.parentNode.innerHTML;
32186         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32187         return src; 
32188     },
32189     /**
32190      * 
32191      * @return {Boolean} this.isConfirmed
32192      */
32193     getConfirmed : function(){
32194         return this.isConfirmed;
32195     },
32196     /**
32197      * 
32198      * @return {Number} this.width
32199      */
32200     getWidth : function(){
32201         return this.width;
32202     },
32203     /**
32204      * 
32205      * @return {Number} this.height
32206      */
32207     getHeight : function(){
32208         return this.height;
32209     },
32210     // private
32211     getSignature : function(){
32212         return this.signatureTmp;
32213     },
32214     // private
32215     reset : function(){
32216         this.signatureTmp = '';
32217         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32218         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32219         this.isConfirmed = false;
32220         Roo.form.Signature.superclass.reset.call(this);
32221     },
32222     setSignature : function(s){
32223         this.signatureTmp = s;
32224         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32225         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32226         this.setValue(s);
32227         this.isConfirmed = false;
32228         Roo.form.Signature.superclass.reset.call(this);
32229     }, 
32230     test : function(){
32231 //        Roo.log(this.signPanel.dom.contentWindow.up())
32232     },
32233     //private
32234     setConfirmed : function(){
32235         
32236         
32237         
32238 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32239     },
32240     // private
32241     confirmHandler : function(){
32242         if(!this.getSignature()){
32243             return;
32244         }
32245         
32246         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32247         this.setValue(this.getSignature());
32248         this.isConfirmed = true;
32249         
32250         this.fireEvent('confirm', this);
32251     },
32252     // private
32253     // Subclasses should provide the validation implementation by overriding this
32254     validateValue : function(value){
32255         if(this.allowBlank){
32256             return true;
32257         }
32258         
32259         if(this.isConfirmed){
32260             return true;
32261         }
32262         return false;
32263     }
32264 });/*
32265  * Based on:
32266  * Ext JS Library 1.1.1
32267  * Copyright(c) 2006-2007, Ext JS, LLC.
32268  *
32269  * Originally Released Under LGPL - original licence link has changed is not relivant.
32270  *
32271  * Fork - LGPL
32272  * <script type="text/javascript">
32273  */
32274  
32275
32276 /**
32277  * @class Roo.form.ComboBox
32278  * @extends Roo.form.TriggerField
32279  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32280  * @constructor
32281  * Create a new ComboBox.
32282  * @param {Object} config Configuration options
32283  */
32284 Roo.form.Select = function(config){
32285     Roo.form.Select.superclass.constructor.call(this, config);
32286      
32287 };
32288
32289 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32290     /**
32291      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32292      */
32293     /**
32294      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32295      * rendering into an Roo.Editor, defaults to false)
32296      */
32297     /**
32298      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32299      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32300      */
32301     /**
32302      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32303      */
32304     /**
32305      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32306      * the dropdown list (defaults to undefined, with no header element)
32307      */
32308
32309      /**
32310      * @cfg {String/Roo.Template} tpl The template to use to render the output
32311      */
32312      
32313     // private
32314     defaultAutoCreate : {tag: "select"  },
32315     /**
32316      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32317      */
32318     listWidth: undefined,
32319     /**
32320      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32321      * mode = 'remote' or 'text' if mode = 'local')
32322      */
32323     displayField: undefined,
32324     /**
32325      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32326      * mode = 'remote' or 'value' if mode = 'local'). 
32327      * Note: use of a valueField requires the user make a selection
32328      * in order for a value to be mapped.
32329      */
32330     valueField: undefined,
32331     
32332     
32333     /**
32334      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32335      * field's data value (defaults to the underlying DOM element's name)
32336      */
32337     hiddenName: undefined,
32338     /**
32339      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32340      */
32341     listClass: '',
32342     /**
32343      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32344      */
32345     selectedClass: 'x-combo-selected',
32346     /**
32347      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
32348      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32349      * which displays a downward arrow icon).
32350      */
32351     triggerClass : 'x-form-arrow-trigger',
32352     /**
32353      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32354      */
32355     shadow:'sides',
32356     /**
32357      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32358      * anchor positions (defaults to 'tl-bl')
32359      */
32360     listAlign: 'tl-bl?',
32361     /**
32362      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32363      */
32364     maxHeight: 300,
32365     /**
32366      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
32367      * query specified by the allQuery config option (defaults to 'query')
32368      */
32369     triggerAction: 'query',
32370     /**
32371      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32372      * (defaults to 4, does not apply if editable = false)
32373      */
32374     minChars : 4,
32375     /**
32376      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32377      * delay (typeAheadDelay) if it matches a known value (defaults to false)
32378      */
32379     typeAhead: false,
32380     /**
32381      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32382      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32383      */
32384     queryDelay: 500,
32385     /**
32386      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32387      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
32388      */
32389     pageSize: 0,
32390     /**
32391      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
32392      * when editable = true (defaults to false)
32393      */
32394     selectOnFocus:false,
32395     /**
32396      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32397      */
32398     queryParam: 'query',
32399     /**
32400      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
32401      * when mode = 'remote' (defaults to 'Loading...')
32402      */
32403     loadingText: 'Loading...',
32404     /**
32405      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32406      */
32407     resizable: false,
32408     /**
32409      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32410      */
32411     handleHeight : 8,
32412     /**
32413      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32414      * traditional select (defaults to true)
32415      */
32416     editable: true,
32417     /**
32418      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32419      */
32420     allQuery: '',
32421     /**
32422      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32423      */
32424     mode: 'remote',
32425     /**
32426      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32427      * listWidth has a higher value)
32428      */
32429     minListWidth : 70,
32430     /**
32431      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32432      * allow the user to set arbitrary text into the field (defaults to false)
32433      */
32434     forceSelection:false,
32435     /**
32436      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32437      * if typeAhead = true (defaults to 250)
32438      */
32439     typeAheadDelay : 250,
32440     /**
32441      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32442      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32443      */
32444     valueNotFoundText : undefined,
32445     
32446     /**
32447      * @cfg {String} defaultValue The value displayed after loading the store.
32448      */
32449     defaultValue: '',
32450     
32451     /**
32452      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32453      */
32454     blockFocus : false,
32455     
32456     /**
32457      * @cfg {Boolean} disableClear Disable showing of clear button.
32458      */
32459     disableClear : false,
32460     /**
32461      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
32462      */
32463     alwaysQuery : false,
32464     
32465     //private
32466     addicon : false,
32467     editicon: false,
32468     
32469     // element that contains real text value.. (when hidden is used..)
32470      
32471     // private
32472     onRender : function(ct, position){
32473         Roo.form.Field.prototype.onRender.call(this, ct, position);
32474         
32475         if(this.store){
32476             this.store.on('beforeload', this.onBeforeLoad, this);
32477             this.store.on('load', this.onLoad, this);
32478             this.store.on('loadexception', this.onLoadException, this);
32479             this.store.load({});
32480         }
32481         
32482         
32483         
32484     },
32485
32486     // private
32487     initEvents : function(){
32488         //Roo.form.ComboBox.superclass.initEvents.call(this);
32489  
32490     },
32491
32492     onDestroy : function(){
32493        
32494         if(this.store){
32495             this.store.un('beforeload', this.onBeforeLoad, this);
32496             this.store.un('load', this.onLoad, this);
32497             this.store.un('loadexception', this.onLoadException, this);
32498         }
32499         //Roo.form.ComboBox.superclass.onDestroy.call(this);
32500     },
32501
32502     // private
32503     fireKey : function(e){
32504         if(e.isNavKeyPress() && !this.list.isVisible()){
32505             this.fireEvent("specialkey", this, e);
32506         }
32507     },
32508
32509     // private
32510     onResize: function(w, h){
32511         
32512         return; 
32513     
32514         
32515     },
32516
32517     /**
32518      * Allow or prevent the user from directly editing the field text.  If false is passed,
32519      * the user will only be able to select from the items defined in the dropdown list.  This method
32520      * is the runtime equivalent of setting the 'editable' config option at config time.
32521      * @param {Boolean} value True to allow the user to directly edit the field text
32522      */
32523     setEditable : function(value){
32524          
32525     },
32526
32527     // private
32528     onBeforeLoad : function(){
32529         
32530         Roo.log("Select before load");
32531         return;
32532     
32533         this.innerList.update(this.loadingText ?
32534                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32535         //this.restrictHeight();
32536         this.selectedIndex = -1;
32537     },
32538
32539     // private
32540     onLoad : function(){
32541
32542     
32543         var dom = this.el.dom;
32544         dom.innerHTML = '';
32545          var od = dom.ownerDocument;
32546          
32547         if (this.emptyText) {
32548             var op = od.createElement('option');
32549             op.setAttribute('value', '');
32550             op.innerHTML = String.format('{0}', this.emptyText);
32551             dom.appendChild(op);
32552         }
32553         if(this.store.getCount() > 0){
32554            
32555             var vf = this.valueField;
32556             var df = this.displayField;
32557             this.store.data.each(function(r) {
32558                 // which colmsn to use... testing - cdoe / title..
32559                 var op = od.createElement('option');
32560                 op.setAttribute('value', r.data[vf]);
32561                 op.innerHTML = String.format('{0}', r.data[df]);
32562                 dom.appendChild(op);
32563             });
32564             if (typeof(this.defaultValue != 'undefined')) {
32565                 this.setValue(this.defaultValue);
32566             }
32567             
32568              
32569         }else{
32570             //this.onEmptyResults();
32571         }
32572         //this.el.focus();
32573     },
32574     // private
32575     onLoadException : function()
32576     {
32577         dom.innerHTML = '';
32578             
32579         Roo.log("Select on load exception");
32580         return;
32581     
32582         this.collapse();
32583         Roo.log(this.store.reader.jsonData);
32584         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32585             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32586         }
32587         
32588         
32589     },
32590     // private
32591     onTypeAhead : function(){
32592          
32593     },
32594
32595     // private
32596     onSelect : function(record, index){
32597         Roo.log('on select?');
32598         return;
32599         if(this.fireEvent('beforeselect', this, record, index) !== false){
32600             this.setFromData(index > -1 ? record.data : false);
32601             this.collapse();
32602             this.fireEvent('select', this, record, index);
32603         }
32604     },
32605
32606     /**
32607      * Returns the currently selected field value or empty string if no value is set.
32608      * @return {String} value The selected value
32609      */
32610     getValue : function(){
32611         var dom = this.el.dom;
32612         this.value = dom.options[dom.selectedIndex].value;
32613         return this.value;
32614         
32615     },
32616
32617     /**
32618      * Clears any text/value currently set in the field
32619      */
32620     clearValue : function(){
32621         this.value = '';
32622         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32623         
32624     },
32625
32626     /**
32627      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32628      * will be displayed in the field.  If the value does not match the data value of an existing item,
32629      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32630      * Otherwise the field will be blank (although the value will still be set).
32631      * @param {String} value The value to match
32632      */
32633     setValue : function(v){
32634         var d = this.el.dom;
32635         for (var i =0; i < d.options.length;i++) {
32636             if (v == d.options[i].value) {
32637                 d.selectedIndex = i;
32638                 this.value = v;
32639                 return;
32640             }
32641         }
32642         this.clearValue();
32643     },
32644     /**
32645      * @property {Object} the last set data for the element
32646      */
32647     
32648     lastData : false,
32649     /**
32650      * Sets the value of the field based on a object which is related to the record format for the store.
32651      * @param {Object} value the value to set as. or false on reset?
32652      */
32653     setFromData : function(o){
32654         Roo.log('setfrom data?');
32655          
32656         
32657         
32658     },
32659     // private
32660     reset : function(){
32661         this.clearValue();
32662     },
32663     // private
32664     findRecord : function(prop, value){
32665         
32666         return false;
32667     
32668         var record;
32669         if(this.store.getCount() > 0){
32670             this.store.each(function(r){
32671                 if(r.data[prop] == value){
32672                     record = r;
32673                     return false;
32674                 }
32675                 return true;
32676             });
32677         }
32678         return record;
32679     },
32680     
32681     getName: function()
32682     {
32683         // returns hidden if it's set..
32684         if (!this.rendered) {return ''};
32685         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32686         
32687     },
32688      
32689
32690     
32691
32692     // private
32693     onEmptyResults : function(){
32694         Roo.log('empty results');
32695         //this.collapse();
32696     },
32697
32698     /**
32699      * Returns true if the dropdown list is expanded, else false.
32700      */
32701     isExpanded : function(){
32702         return false;
32703     },
32704
32705     /**
32706      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32707      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32708      * @param {String} value The data value of the item to select
32709      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32710      * selected item if it is not currently in view (defaults to true)
32711      * @return {Boolean} True if the value matched an item in the list, else false
32712      */
32713     selectByValue : function(v, scrollIntoView){
32714         Roo.log('select By Value');
32715         return false;
32716     
32717         if(v !== undefined && v !== null){
32718             var r = this.findRecord(this.valueField || this.displayField, v);
32719             if(r){
32720                 this.select(this.store.indexOf(r), scrollIntoView);
32721                 return true;
32722             }
32723         }
32724         return false;
32725     },
32726
32727     /**
32728      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32729      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32730      * @param {Number} index The zero-based index of the list item to select
32731      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32732      * selected item if it is not currently in view (defaults to true)
32733      */
32734     select : function(index, scrollIntoView){
32735         Roo.log('select ');
32736         return  ;
32737         
32738         this.selectedIndex = index;
32739         this.view.select(index);
32740         if(scrollIntoView !== false){
32741             var el = this.view.getNode(index);
32742             if(el){
32743                 this.innerList.scrollChildIntoView(el, false);
32744             }
32745         }
32746     },
32747
32748       
32749
32750     // private
32751     validateBlur : function(){
32752         
32753         return;
32754         
32755     },
32756
32757     // private
32758     initQuery : function(){
32759         this.doQuery(this.getRawValue());
32760     },
32761
32762     // private
32763     doForce : function(){
32764         if(this.el.dom.value.length > 0){
32765             this.el.dom.value =
32766                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32767              
32768         }
32769     },
32770
32771     /**
32772      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32773      * query allowing the query action to be canceled if needed.
32774      * @param {String} query The SQL query to execute
32775      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32776      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32777      * saved in the current store (defaults to false)
32778      */
32779     doQuery : function(q, forceAll){
32780         
32781         Roo.log('doQuery?');
32782         if(q === undefined || q === null){
32783             q = '';
32784         }
32785         var qe = {
32786             query: q,
32787             forceAll: forceAll,
32788             combo: this,
32789             cancel:false
32790         };
32791         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32792             return false;
32793         }
32794         q = qe.query;
32795         forceAll = qe.forceAll;
32796         if(forceAll === true || (q.length >= this.minChars)){
32797             if(this.lastQuery != q || this.alwaysQuery){
32798                 this.lastQuery = q;
32799                 if(this.mode == 'local'){
32800                     this.selectedIndex = -1;
32801                     if(forceAll){
32802                         this.store.clearFilter();
32803                     }else{
32804                         this.store.filter(this.displayField, q);
32805                     }
32806                     this.onLoad();
32807                 }else{
32808                     this.store.baseParams[this.queryParam] = q;
32809                     this.store.load({
32810                         params: this.getParams(q)
32811                     });
32812                     this.expand();
32813                 }
32814             }else{
32815                 this.selectedIndex = -1;
32816                 this.onLoad();   
32817             }
32818         }
32819     },
32820
32821     // private
32822     getParams : function(q){
32823         var p = {};
32824         //p[this.queryParam] = q;
32825         if(this.pageSize){
32826             p.start = 0;
32827             p.limit = this.pageSize;
32828         }
32829         return p;
32830     },
32831
32832     /**
32833      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32834      */
32835     collapse : function(){
32836         
32837     },
32838
32839     // private
32840     collapseIf : function(e){
32841         
32842     },
32843
32844     /**
32845      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32846      */
32847     expand : function(){
32848         
32849     } ,
32850
32851     // private
32852      
32853
32854     /** 
32855     * @cfg {Boolean} grow 
32856     * @hide 
32857     */
32858     /** 
32859     * @cfg {Number} growMin 
32860     * @hide 
32861     */
32862     /** 
32863     * @cfg {Number} growMax 
32864     * @hide 
32865     */
32866     /**
32867      * @hide
32868      * @method autoSize
32869      */
32870     
32871     setWidth : function()
32872     {
32873         
32874     },
32875     getResizeEl : function(){
32876         return this.el;
32877     }
32878 });//<script type="text/javasscript">
32879  
32880
32881 /**
32882  * @class Roo.DDView
32883  * A DnD enabled version of Roo.View.
32884  * @param {Element/String} container The Element in which to create the View.
32885  * @param {String} tpl The template string used to create the markup for each element of the View
32886  * @param {Object} config The configuration properties. These include all the config options of
32887  * {@link Roo.View} plus some specific to this class.<br>
32888  * <p>
32889  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32890  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32891  * <p>
32892  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32893 .x-view-drag-insert-above {
32894         border-top:1px dotted #3366cc;
32895 }
32896 .x-view-drag-insert-below {
32897         border-bottom:1px dotted #3366cc;
32898 }
32899 </code></pre>
32900  * 
32901  */
32902  
32903 Roo.DDView = function(container, tpl, config) {
32904     Roo.DDView.superclass.constructor.apply(this, arguments);
32905     this.getEl().setStyle("outline", "0px none");
32906     this.getEl().unselectable();
32907     if (this.dragGroup) {
32908         this.setDraggable(this.dragGroup.split(","));
32909     }
32910     if (this.dropGroup) {
32911         this.setDroppable(this.dropGroup.split(","));
32912     }
32913     if (this.deletable) {
32914         this.setDeletable();
32915     }
32916     this.isDirtyFlag = false;
32917         this.addEvents({
32918                 "drop" : true
32919         });
32920 };
32921
32922 Roo.extend(Roo.DDView, Roo.View, {
32923 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32924 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32925 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32926 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32927
32928         isFormField: true,
32929
32930         reset: Roo.emptyFn,
32931         
32932         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32933
32934         validate: function() {
32935                 return true;
32936         },
32937         
32938         destroy: function() {
32939                 this.purgeListeners();
32940                 this.getEl.removeAllListeners();
32941                 this.getEl().remove();
32942                 if (this.dragZone) {
32943                         if (this.dragZone.destroy) {
32944                                 this.dragZone.destroy();
32945                         }
32946                 }
32947                 if (this.dropZone) {
32948                         if (this.dropZone.destroy) {
32949                                 this.dropZone.destroy();
32950                         }
32951                 }
32952         },
32953
32954 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32955         getName: function() {
32956                 return this.name;
32957         },
32958
32959 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32960         setValue: function(v) {
32961                 if (!this.store) {
32962                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32963                 }
32964                 var data = {};
32965                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32966                 this.store.proxy = new Roo.data.MemoryProxy(data);
32967                 this.store.load();
32968         },
32969
32970 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32971         getValue: function() {
32972                 var result = '(';
32973                 this.store.each(function(rec) {
32974                         result += rec.id + ',';
32975                 });
32976                 return result.substr(0, result.length - 1) + ')';
32977         },
32978         
32979         getIds: function() {
32980                 var i = 0, result = new Array(this.store.getCount());
32981                 this.store.each(function(rec) {
32982                         result[i++] = rec.id;
32983                 });
32984                 return result;
32985         },
32986         
32987         isDirty: function() {
32988                 return this.isDirtyFlag;
32989         },
32990
32991 /**
32992  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32993  *      whole Element becomes the target, and this causes the drop gesture to append.
32994  */
32995     getTargetFromEvent : function(e) {
32996                 var target = e.getTarget();
32997                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32998                 target = target.parentNode;
32999                 }
33000                 if (!target) {
33001                         target = this.el.dom.lastChild || this.el.dom;
33002                 }
33003                 return target;
33004     },
33005
33006 /**
33007  *      Create the drag data which consists of an object which has the property "ddel" as
33008  *      the drag proxy element. 
33009  */
33010     getDragData : function(e) {
33011         var target = this.findItemFromChild(e.getTarget());
33012                 if(target) {
33013                         this.handleSelection(e);
33014                         var selNodes = this.getSelectedNodes();
33015             var dragData = {
33016                 source: this,
33017                 copy: this.copy || (this.allowCopy && e.ctrlKey),
33018                 nodes: selNodes,
33019                 records: []
33020                         };
33021                         var selectedIndices = this.getSelectedIndexes();
33022                         for (var i = 0; i < selectedIndices.length; i++) {
33023                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
33024                         }
33025                         if (selNodes.length == 1) {
33026                                 dragData.ddel = target.cloneNode(true); // the div element
33027                         } else {
33028                                 var div = document.createElement('div'); // create the multi element drag "ghost"
33029                                 div.className = 'multi-proxy';
33030                                 for (var i = 0, len = selNodes.length; i < len; i++) {
33031                                         div.appendChild(selNodes[i].cloneNode(true));
33032                                 }
33033                                 dragData.ddel = div;
33034                         }
33035             //console.log(dragData)
33036             //console.log(dragData.ddel.innerHTML)
33037                         return dragData;
33038                 }
33039         //console.log('nodragData')
33040                 return false;
33041     },
33042     
33043 /**     Specify to which ddGroup items in this DDView may be dragged. */
33044     setDraggable: function(ddGroup) {
33045         if (ddGroup instanceof Array) {
33046                 Roo.each(ddGroup, this.setDraggable, this);
33047                 return;
33048         }
33049         if (this.dragZone) {
33050                 this.dragZone.addToGroup(ddGroup);
33051         } else {
33052                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33053                                 containerScroll: true,
33054                                 ddGroup: ddGroup 
33055
33056                         });
33057 //                      Draggability implies selection. DragZone's mousedown selects the element.
33058                         if (!this.multiSelect) { this.singleSelect = true; }
33059
33060 //                      Wire the DragZone's handlers up to methods in *this*
33061                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
33062                 }
33063     },
33064
33065 /**     Specify from which ddGroup this DDView accepts drops. */
33066     setDroppable: function(ddGroup) {
33067         if (ddGroup instanceof Array) {
33068                 Roo.each(ddGroup, this.setDroppable, this);
33069                 return;
33070         }
33071         if (this.dropZone) {
33072                 this.dropZone.addToGroup(ddGroup);
33073         } else {
33074                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33075                                 containerScroll: true,
33076                                 ddGroup: ddGroup
33077                         });
33078
33079 //                      Wire the DropZone's handlers up to methods in *this*
33080                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33081                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33082                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33083                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33084                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33085                 }
33086     },
33087
33088 /**     Decide whether to drop above or below a View node. */
33089     getDropPoint : function(e, n, dd){
33090         if (n == this.el.dom) { return "above"; }
33091                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33092                 var c = t + (b - t) / 2;
33093                 var y = Roo.lib.Event.getPageY(e);
33094                 if(y <= c) {
33095                         return "above";
33096                 }else{
33097                         return "below";
33098                 }
33099     },
33100
33101     onNodeEnter : function(n, dd, e, data){
33102                 return false;
33103     },
33104     
33105     onNodeOver : function(n, dd, e, data){
33106                 var pt = this.getDropPoint(e, n, dd);
33107                 // set the insert point style on the target node
33108                 var dragElClass = this.dropNotAllowed;
33109                 if (pt) {
33110                         var targetElClass;
33111                         if (pt == "above"){
33112                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33113                                 targetElClass = "x-view-drag-insert-above";
33114                         } else {
33115                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33116                                 targetElClass = "x-view-drag-insert-below";
33117                         }
33118                         if (this.lastInsertClass != targetElClass){
33119                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33120                                 this.lastInsertClass = targetElClass;
33121                         }
33122                 }
33123                 return dragElClass;
33124         },
33125
33126     onNodeOut : function(n, dd, e, data){
33127                 this.removeDropIndicators(n);
33128     },
33129
33130     onNodeDrop : function(n, dd, e, data){
33131         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33132                 return false;
33133         }
33134         var pt = this.getDropPoint(e, n, dd);
33135                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33136                 if (pt == "below") { insertAt++; }
33137                 for (var i = 0; i < data.records.length; i++) {
33138                         var r = data.records[i];
33139                         var dup = this.store.getById(r.id);
33140                         if (dup && (dd != this.dragZone)) {
33141                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33142                         } else {
33143                                 if (data.copy) {
33144                                         this.store.insert(insertAt++, r.copy());
33145                                 } else {
33146                                         data.source.isDirtyFlag = true;
33147                                         r.store.remove(r);
33148                                         this.store.insert(insertAt++, r);
33149                                 }
33150                                 this.isDirtyFlag = true;
33151                         }
33152                 }
33153                 this.dragZone.cachedTarget = null;
33154                 return true;
33155     },
33156
33157     removeDropIndicators : function(n){
33158                 if(n){
33159                         Roo.fly(n).removeClass([
33160                                 "x-view-drag-insert-above",
33161                                 "x-view-drag-insert-below"]);
33162                         this.lastInsertClass = "_noclass";
33163                 }
33164     },
33165
33166 /**
33167  *      Utility method. Add a delete option to the DDView's context menu.
33168  *      @param {String} imageUrl The URL of the "delete" icon image.
33169  */
33170         setDeletable: function(imageUrl) {
33171                 if (!this.singleSelect && !this.multiSelect) {
33172                         this.singleSelect = true;
33173                 }
33174                 var c = this.getContextMenu();
33175                 this.contextMenu.on("itemclick", function(item) {
33176                         switch (item.id) {
33177                                 case "delete":
33178                                         this.remove(this.getSelectedIndexes());
33179                                         break;
33180                         }
33181                 }, this);
33182                 this.contextMenu.add({
33183                         icon: imageUrl,
33184                         id: "delete",
33185                         text: 'Delete'
33186                 });
33187         },
33188         
33189 /**     Return the context menu for this DDView. */
33190         getContextMenu: function() {
33191                 if (!this.contextMenu) {
33192 //                      Create the View's context menu
33193                         this.contextMenu = new Roo.menu.Menu({
33194                                 id: this.id + "-contextmenu"
33195                         });
33196                         this.el.on("contextmenu", this.showContextMenu, this);
33197                 }
33198                 return this.contextMenu;
33199         },
33200         
33201         disableContextMenu: function() {
33202                 if (this.contextMenu) {
33203                         this.el.un("contextmenu", this.showContextMenu, this);
33204                 }
33205         },
33206
33207         showContextMenu: function(e, item) {
33208         item = this.findItemFromChild(e.getTarget());
33209                 if (item) {
33210                         e.stopEvent();
33211                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33212                         this.contextMenu.showAt(e.getXY());
33213             }
33214     },
33215
33216 /**
33217  *      Remove {@link Roo.data.Record}s at the specified indices.
33218  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33219  */
33220     remove: function(selectedIndices) {
33221                 selectedIndices = [].concat(selectedIndices);
33222                 for (var i = 0; i < selectedIndices.length; i++) {
33223                         var rec = this.store.getAt(selectedIndices[i]);
33224                         this.store.remove(rec);
33225                 }
33226     },
33227
33228 /**
33229  *      Double click fires the event, but also, if this is draggable, and there is only one other
33230  *      related DropZone, it transfers the selected node.
33231  */
33232     onDblClick : function(e){
33233         var item = this.findItemFromChild(e.getTarget());
33234         if(item){
33235             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33236                 return false;
33237             }
33238             if (this.dragGroup) {
33239                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33240                     while (targets.indexOf(this.dropZone) > -1) {
33241                             targets.remove(this.dropZone);
33242                                 }
33243                     if (targets.length == 1) {
33244                                         this.dragZone.cachedTarget = null;
33245                         var el = Roo.get(targets[0].getEl());
33246                         var box = el.getBox(true);
33247                         targets[0].onNodeDrop(el.dom, {
33248                                 target: el.dom,
33249                                 xy: [box.x, box.y + box.height - 1]
33250                         }, null, this.getDragData(e));
33251                     }
33252                 }
33253         }
33254     },
33255     
33256     handleSelection: function(e) {
33257                 this.dragZone.cachedTarget = null;
33258         var item = this.findItemFromChild(e.getTarget());
33259         if (!item) {
33260                 this.clearSelections(true);
33261                 return;
33262         }
33263                 if (item && (this.multiSelect || this.singleSelect)){
33264                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33265                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33266                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33267                                 this.unselect(item);
33268                         } else {
33269                                 this.select(item, this.multiSelect && e.ctrlKey);
33270                                 this.lastSelection = item;
33271                         }
33272                 }
33273     },
33274
33275     onItemClick : function(item, index, e){
33276                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33277                         return false;
33278                 }
33279                 return true;
33280     },
33281
33282     unselect : function(nodeInfo, suppressEvent){
33283                 var node = this.getNode(nodeInfo);
33284                 if(node && this.isSelected(node)){
33285                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33286                                 Roo.fly(node).removeClass(this.selectedClass);
33287                                 this.selections.remove(node);
33288                                 if(!suppressEvent){
33289                                         this.fireEvent("selectionchange", this, this.selections);
33290                                 }
33291                         }
33292                 }
33293     }
33294 });
33295 /*
33296  * Based on:
33297  * Ext JS Library 1.1.1
33298  * Copyright(c) 2006-2007, Ext JS, LLC.
33299  *
33300  * Originally Released Under LGPL - original licence link has changed is not relivant.
33301  *
33302  * Fork - LGPL
33303  * <script type="text/javascript">
33304  */
33305  
33306 /**
33307  * @class Roo.LayoutManager
33308  * @extends Roo.util.Observable
33309  * Base class for layout managers.
33310  */
33311 Roo.LayoutManager = function(container, config){
33312     Roo.LayoutManager.superclass.constructor.call(this);
33313     this.el = Roo.get(container);
33314     // ie scrollbar fix
33315     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33316         document.body.scroll = "no";
33317     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33318         this.el.position('relative');
33319     }
33320     this.id = this.el.id;
33321     this.el.addClass("x-layout-container");
33322     /** false to disable window resize monitoring @type Boolean */
33323     this.monitorWindowResize = true;
33324     this.regions = {};
33325     this.addEvents({
33326         /**
33327          * @event layout
33328          * Fires when a layout is performed. 
33329          * @param {Roo.LayoutManager} this
33330          */
33331         "layout" : true,
33332         /**
33333          * @event regionresized
33334          * Fires when the user resizes a region. 
33335          * @param {Roo.LayoutRegion} region The resized region
33336          * @param {Number} newSize The new size (width for east/west, height for north/south)
33337          */
33338         "regionresized" : true,
33339         /**
33340          * @event regioncollapsed
33341          * Fires when a region is collapsed. 
33342          * @param {Roo.LayoutRegion} region The collapsed region
33343          */
33344         "regioncollapsed" : true,
33345         /**
33346          * @event regionexpanded
33347          * Fires when a region is expanded.  
33348          * @param {Roo.LayoutRegion} region The expanded region
33349          */
33350         "regionexpanded" : true
33351     });
33352     this.updating = false;
33353     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33354 };
33355
33356 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33357     /**
33358      * Returns true if this layout is currently being updated
33359      * @return {Boolean}
33360      */
33361     isUpdating : function(){
33362         return this.updating; 
33363     },
33364     
33365     /**
33366      * Suspend the LayoutManager from doing auto-layouts while
33367      * making multiple add or remove calls
33368      */
33369     beginUpdate : function(){
33370         this.updating = true;    
33371     },
33372     
33373     /**
33374      * Restore auto-layouts and optionally disable the manager from performing a layout
33375      * @param {Boolean} noLayout true to disable a layout update 
33376      */
33377     endUpdate : function(noLayout){
33378         this.updating = false;
33379         if(!noLayout){
33380             this.layout();
33381         }    
33382     },
33383     
33384     layout: function(){
33385         
33386     },
33387     
33388     onRegionResized : function(region, newSize){
33389         this.fireEvent("regionresized", region, newSize);
33390         this.layout();
33391     },
33392     
33393     onRegionCollapsed : function(region){
33394         this.fireEvent("regioncollapsed", region);
33395     },
33396     
33397     onRegionExpanded : function(region){
33398         this.fireEvent("regionexpanded", region);
33399     },
33400         
33401     /**
33402      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33403      * performs box-model adjustments.
33404      * @return {Object} The size as an object {width: (the width), height: (the height)}
33405      */
33406     getViewSize : function(){
33407         var size;
33408         if(this.el.dom != document.body){
33409             size = this.el.getSize();
33410         }else{
33411             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33412         }
33413         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33414         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33415         return size;
33416     },
33417     
33418     /**
33419      * Returns the Element this layout is bound to.
33420      * @return {Roo.Element}
33421      */
33422     getEl : function(){
33423         return this.el;
33424     },
33425     
33426     /**
33427      * Returns the specified region.
33428      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33429      * @return {Roo.LayoutRegion}
33430      */
33431     getRegion : function(target){
33432         return this.regions[target.toLowerCase()];
33433     },
33434     
33435     onWindowResize : function(){
33436         if(this.monitorWindowResize){
33437             this.layout();
33438         }
33439     }
33440 });/*
33441  * Based on:
33442  * Ext JS Library 1.1.1
33443  * Copyright(c) 2006-2007, Ext JS, LLC.
33444  *
33445  * Originally Released Under LGPL - original licence link has changed is not relivant.
33446  *
33447  * Fork - LGPL
33448  * <script type="text/javascript">
33449  */
33450 /**
33451  * @class Roo.BorderLayout
33452  * @extends Roo.LayoutManager
33453  * @children Roo.ContentPanel
33454  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33455  * please see: <br><br>
33456  * <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>
33457  * <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>
33458  * Example:
33459  <pre><code>
33460  var layout = new Roo.BorderLayout(document.body, {
33461     north: {
33462         initialSize: 25,
33463         titlebar: false
33464     },
33465     west: {
33466         split:true,
33467         initialSize: 200,
33468         minSize: 175,
33469         maxSize: 400,
33470         titlebar: true,
33471         collapsible: true
33472     },
33473     east: {
33474         split:true,
33475         initialSize: 202,
33476         minSize: 175,
33477         maxSize: 400,
33478         titlebar: true,
33479         collapsible: true
33480     },
33481     south: {
33482         split:true,
33483         initialSize: 100,
33484         minSize: 100,
33485         maxSize: 200,
33486         titlebar: true,
33487         collapsible: true
33488     },
33489     center: {
33490         titlebar: true,
33491         autoScroll:true,
33492         resizeTabs: true,
33493         minTabWidth: 50,
33494         preferredTabWidth: 150
33495     }
33496 });
33497
33498 // shorthand
33499 var CP = Roo.ContentPanel;
33500
33501 layout.beginUpdate();
33502 layout.add("north", new CP("north", "North"));
33503 layout.add("south", new CP("south", {title: "South", closable: true}));
33504 layout.add("west", new CP("west", {title: "West"}));
33505 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33506 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33507 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33508 layout.getRegion("center").showPanel("center1");
33509 layout.endUpdate();
33510 </code></pre>
33511
33512 <b>The container the layout is rendered into can be either the body element or any other element.
33513 If it is not the body element, the container needs to either be an absolute positioned element,
33514 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33515 the container size if it is not the body element.</b>
33516
33517 * @constructor
33518 * Create a new BorderLayout
33519 * @param {String/HTMLElement/Element} container The container this layout is bound to
33520 * @param {Object} config Configuration options
33521  */
33522 Roo.BorderLayout = function(container, config){
33523     config = config || {};
33524     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33525     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33526     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33527         var target = this.factory.validRegions[i];
33528         if(config[target]){
33529             this.addRegion(target, config[target]);
33530         }
33531     }
33532 };
33533
33534 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33535         
33536         /**
33537          * @cfg {Roo.LayoutRegion} east
33538          */
33539         /**
33540          * @cfg {Roo.LayoutRegion} west
33541          */
33542         /**
33543          * @cfg {Roo.LayoutRegion} north
33544          */
33545         /**
33546          * @cfg {Roo.LayoutRegion} south
33547          */
33548         /**
33549          * @cfg {Roo.LayoutRegion} center
33550          */
33551     /**
33552      * Creates and adds a new region if it doesn't already exist.
33553      * @param {String} target The target region key (north, south, east, west or center).
33554      * @param {Object} config The regions config object
33555      * @return {BorderLayoutRegion} The new region
33556      */
33557     addRegion : function(target, config){
33558         if(!this.regions[target]){
33559             var r = this.factory.create(target, this, config);
33560             this.bindRegion(target, r);
33561         }
33562         return this.regions[target];
33563     },
33564
33565     // private (kinda)
33566     bindRegion : function(name, r){
33567         this.regions[name] = r;
33568         r.on("visibilitychange", this.layout, this);
33569         r.on("paneladded", this.layout, this);
33570         r.on("panelremoved", this.layout, this);
33571         r.on("invalidated", this.layout, this);
33572         r.on("resized", this.onRegionResized, this);
33573         r.on("collapsed", this.onRegionCollapsed, this);
33574         r.on("expanded", this.onRegionExpanded, this);
33575     },
33576
33577     /**
33578      * Performs a layout update.
33579      */
33580     layout : function(){
33581         if(this.updating) {
33582             return;
33583         }
33584         var size = this.getViewSize();
33585         var w = size.width;
33586         var h = size.height;
33587         var centerW = w;
33588         var centerH = h;
33589         var centerY = 0;
33590         var centerX = 0;
33591         //var x = 0, y = 0;
33592
33593         var rs = this.regions;
33594         var north = rs["north"];
33595         var south = rs["south"]; 
33596         var west = rs["west"];
33597         var east = rs["east"];
33598         var center = rs["center"];
33599         //if(this.hideOnLayout){ // not supported anymore
33600             //c.el.setStyle("display", "none");
33601         //}
33602         if(north && north.isVisible()){
33603             var b = north.getBox();
33604             var m = north.getMargins();
33605             b.width = w - (m.left+m.right);
33606             b.x = m.left;
33607             b.y = m.top;
33608             centerY = b.height + b.y + m.bottom;
33609             centerH -= centerY;
33610             north.updateBox(this.safeBox(b));
33611         }
33612         if(south && south.isVisible()){
33613             var b = south.getBox();
33614             var m = south.getMargins();
33615             b.width = w - (m.left+m.right);
33616             b.x = m.left;
33617             var totalHeight = (b.height + m.top + m.bottom);
33618             b.y = h - totalHeight + m.top;
33619             centerH -= totalHeight;
33620             south.updateBox(this.safeBox(b));
33621         }
33622         if(west && west.isVisible()){
33623             var b = west.getBox();
33624             var m = west.getMargins();
33625             b.height = centerH - (m.top+m.bottom);
33626             b.x = m.left;
33627             b.y = centerY + m.top;
33628             var totalWidth = (b.width + m.left + m.right);
33629             centerX += totalWidth;
33630             centerW -= totalWidth;
33631             west.updateBox(this.safeBox(b));
33632         }
33633         if(east && east.isVisible()){
33634             var b = east.getBox();
33635             var m = east.getMargins();
33636             b.height = centerH - (m.top+m.bottom);
33637             var totalWidth = (b.width + m.left + m.right);
33638             b.x = w - totalWidth + m.left;
33639             b.y = centerY + m.top;
33640             centerW -= totalWidth;
33641             east.updateBox(this.safeBox(b));
33642         }
33643         if(center){
33644             var m = center.getMargins();
33645             var centerBox = {
33646                 x: centerX + m.left,
33647                 y: centerY + m.top,
33648                 width: centerW - (m.left+m.right),
33649                 height: centerH - (m.top+m.bottom)
33650             };
33651             //if(this.hideOnLayout){
33652                 //center.el.setStyle("display", "block");
33653             //}
33654             center.updateBox(this.safeBox(centerBox));
33655         }
33656         this.el.repaint();
33657         this.fireEvent("layout", this);
33658     },
33659
33660     // private
33661     safeBox : function(box){
33662         box.width = Math.max(0, box.width);
33663         box.height = Math.max(0, box.height);
33664         return box;
33665     },
33666
33667     /**
33668      * Adds a ContentPanel (or subclass) to this layout.
33669      * @param {String} target The target region key (north, south, east, west or center).
33670      * @param {Roo.ContentPanel} panel The panel to add
33671      * @return {Roo.ContentPanel} The added panel
33672      */
33673     add : function(target, panel){
33674          
33675         target = target.toLowerCase();
33676         return this.regions[target].add(panel);
33677     },
33678
33679     /**
33680      * Remove a ContentPanel (or subclass) to this layout.
33681      * @param {String} target The target region key (north, south, east, west or center).
33682      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33683      * @return {Roo.ContentPanel} The removed panel
33684      */
33685     remove : function(target, panel){
33686         target = target.toLowerCase();
33687         return this.regions[target].remove(panel);
33688     },
33689
33690     /**
33691      * Searches all regions for a panel with the specified id
33692      * @param {String} panelId
33693      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33694      */
33695     findPanel : function(panelId){
33696         var rs = this.regions;
33697         for(var target in rs){
33698             if(typeof rs[target] != "function"){
33699                 var p = rs[target].getPanel(panelId);
33700                 if(p){
33701                     return p;
33702                 }
33703             }
33704         }
33705         return null;
33706     },
33707
33708     /**
33709      * Searches all regions for a panel with the specified id and activates (shows) it.
33710      * @param {String/ContentPanel} panelId The panels id or the panel itself
33711      * @return {Roo.ContentPanel} The shown panel or null
33712      */
33713     showPanel : function(panelId) {
33714       var rs = this.regions;
33715       for(var target in rs){
33716          var r = rs[target];
33717          if(typeof r != "function"){
33718             if(r.hasPanel(panelId)){
33719                return r.showPanel(panelId);
33720             }
33721          }
33722       }
33723       return null;
33724    },
33725
33726    /**
33727      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33728      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33729      */
33730     restoreState : function(provider){
33731         if(!provider){
33732             provider = Roo.state.Manager;
33733         }
33734         var sm = new Roo.LayoutStateManager();
33735         sm.init(this, provider);
33736     },
33737
33738     /**
33739      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33740      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33741      * a valid ContentPanel config object.  Example:
33742      * <pre><code>
33743 // Create the main layout
33744 var layout = new Roo.BorderLayout('main-ct', {
33745     west: {
33746         split:true,
33747         minSize: 175,
33748         titlebar: true
33749     },
33750     center: {
33751         title:'Components'
33752     }
33753 }, 'main-ct');
33754
33755 // Create and add multiple ContentPanels at once via configs
33756 layout.batchAdd({
33757    west: {
33758        id: 'source-files',
33759        autoCreate:true,
33760        title:'Ext Source Files',
33761        autoScroll:true,
33762        fitToFrame:true
33763    },
33764    center : {
33765        el: cview,
33766        autoScroll:true,
33767        fitToFrame:true,
33768        toolbar: tb,
33769        resizeEl:'cbody'
33770    }
33771 });
33772 </code></pre>
33773      * @param {Object} regions An object containing ContentPanel configs by region name
33774      */
33775     batchAdd : function(regions){
33776         this.beginUpdate();
33777         for(var rname in regions){
33778             var lr = this.regions[rname];
33779             if(lr){
33780                 this.addTypedPanels(lr, regions[rname]);
33781             }
33782         }
33783         this.endUpdate();
33784     },
33785
33786     // private
33787     addTypedPanels : function(lr, ps){
33788         if(typeof ps == 'string'){
33789             lr.add(new Roo.ContentPanel(ps));
33790         }
33791         else if(ps instanceof Array){
33792             for(var i =0, len = ps.length; i < len; i++){
33793                 this.addTypedPanels(lr, ps[i]);
33794             }
33795         }
33796         else if(!ps.events){ // raw config?
33797             var el = ps.el;
33798             delete ps.el; // prevent conflict
33799             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33800         }
33801         else {  // panel object assumed!
33802             lr.add(ps);
33803         }
33804     },
33805     /**
33806      * Adds a xtype elements to the layout.
33807      * <pre><code>
33808
33809 layout.addxtype({
33810        xtype : 'ContentPanel',
33811        region: 'west',
33812        items: [ .... ]
33813    }
33814 );
33815
33816 layout.addxtype({
33817         xtype : 'NestedLayoutPanel',
33818         region: 'west',
33819         layout: {
33820            center: { },
33821            west: { }   
33822         },
33823         items : [ ... list of content panels or nested layout panels.. ]
33824    }
33825 );
33826 </code></pre>
33827      * @param {Object} cfg Xtype definition of item to add.
33828      */
33829     addxtype : function(cfg)
33830     {
33831         // basically accepts a pannel...
33832         // can accept a layout region..!?!?
33833         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33834         
33835         if (!cfg.xtype.match(/Panel$/)) {
33836             return false;
33837         }
33838         var ret = false;
33839         
33840         if (typeof(cfg.region) == 'undefined') {
33841             Roo.log("Failed to add Panel, region was not set");
33842             Roo.log(cfg);
33843             return false;
33844         }
33845         var region = cfg.region;
33846         delete cfg.region;
33847         
33848           
33849         var xitems = [];
33850         if (cfg.items) {
33851             xitems = cfg.items;
33852             delete cfg.items;
33853         }
33854         var nb = false;
33855         
33856         switch(cfg.xtype) 
33857         {
33858             case 'ContentPanel':  // ContentPanel (el, cfg)
33859             case 'ScrollPanel':  // ContentPanel (el, cfg)
33860             case 'ViewPanel': 
33861                 if(cfg.autoCreate) {
33862                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33863                 } else {
33864                     var el = this.el.createChild();
33865                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33866                 }
33867                 
33868                 this.add(region, ret);
33869                 break;
33870             
33871             
33872             case 'TreePanel': // our new panel!
33873                 cfg.el = this.el.createChild();
33874                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33875                 this.add(region, ret);
33876                 break;
33877             
33878             case 'NestedLayoutPanel': 
33879                 // create a new Layout (which is  a Border Layout...
33880                 var el = this.el.createChild();
33881                 var clayout = cfg.layout;
33882                 delete cfg.layout;
33883                 clayout.items   = clayout.items  || [];
33884                 // replace this exitems with the clayout ones..
33885                 xitems = clayout.items;
33886                  
33887                 
33888                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33889                     cfg.background = false;
33890                 }
33891                 var layout = new Roo.BorderLayout(el, clayout);
33892                 
33893                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33894                 //console.log('adding nested layout panel '  + cfg.toSource());
33895                 this.add(region, ret);
33896                 nb = {}; /// find first...
33897                 break;
33898                 
33899             case 'GridPanel': 
33900             
33901                 // needs grid and region
33902                 
33903                 //var el = this.getRegion(region).el.createChild();
33904                 var el = this.el.createChild();
33905                 // create the grid first...
33906                 
33907                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33908                 delete cfg.grid;
33909                 if (region == 'center' && this.active ) {
33910                     cfg.background = false;
33911                 }
33912                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33913                 
33914                 this.add(region, ret);
33915                 if (cfg.background) {
33916                     ret.on('activate', function(gp) {
33917                         if (!gp.grid.rendered) {
33918                             gp.grid.render();
33919                         }
33920                     });
33921                 } else {
33922                     grid.render();
33923                 }
33924                 break;
33925            
33926            
33927            
33928                 
33929                 
33930                 
33931             default:
33932                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33933                     
33934                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33935                     this.add(region, ret);
33936                 } else {
33937                 
33938                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33939                     return null;
33940                 }
33941                 
33942              // GridPanel (grid, cfg)
33943             
33944         }
33945         this.beginUpdate();
33946         // add children..
33947         var region = '';
33948         var abn = {};
33949         Roo.each(xitems, function(i)  {
33950             region = nb && i.region ? i.region : false;
33951             
33952             var add = ret.addxtype(i);
33953            
33954             if (region) {
33955                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33956                 if (!i.background) {
33957                     abn[region] = nb[region] ;
33958                 }
33959             }
33960             
33961         });
33962         this.endUpdate();
33963
33964         // make the last non-background panel active..
33965         //if (nb) { Roo.log(abn); }
33966         if (nb) {
33967             
33968             for(var r in abn) {
33969                 region = this.getRegion(r);
33970                 if (region) {
33971                     // tried using nb[r], but it does not work..
33972                      
33973                     region.showPanel(abn[r]);
33974                    
33975                 }
33976             }
33977         }
33978         return ret;
33979         
33980     }
33981 });
33982
33983 /**
33984  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33985  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33986  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33987  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33988  * <pre><code>
33989 // shorthand
33990 var CP = Roo.ContentPanel;
33991
33992 var layout = Roo.BorderLayout.create({
33993     north: {
33994         initialSize: 25,
33995         titlebar: false,
33996         panels: [new CP("north", "North")]
33997     },
33998     west: {
33999         split:true,
34000         initialSize: 200,
34001         minSize: 175,
34002         maxSize: 400,
34003         titlebar: true,
34004         collapsible: true,
34005         panels: [new CP("west", {title: "West"})]
34006     },
34007     east: {
34008         split:true,
34009         initialSize: 202,
34010         minSize: 175,
34011         maxSize: 400,
34012         titlebar: true,
34013         collapsible: true,
34014         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34015     },
34016     south: {
34017         split:true,
34018         initialSize: 100,
34019         minSize: 100,
34020         maxSize: 200,
34021         titlebar: true,
34022         collapsible: true,
34023         panels: [new CP("south", {title: "South", closable: true})]
34024     },
34025     center: {
34026         titlebar: true,
34027         autoScroll:true,
34028         resizeTabs: true,
34029         minTabWidth: 50,
34030         preferredTabWidth: 150,
34031         panels: [
34032             new CP("center1", {title: "Close Me", closable: true}),
34033             new CP("center2", {title: "Center Panel", closable: false})
34034         ]
34035     }
34036 }, document.body);
34037
34038 layout.getRegion("center").showPanel("center1");
34039 </code></pre>
34040  * @param config
34041  * @param targetEl
34042  */
34043 Roo.BorderLayout.create = function(config, targetEl){
34044     var layout = new Roo.BorderLayout(targetEl || document.body, config);
34045     layout.beginUpdate();
34046     var regions = Roo.BorderLayout.RegionFactory.validRegions;
34047     for(var j = 0, jlen = regions.length; j < jlen; j++){
34048         var lr = regions[j];
34049         if(layout.regions[lr] && config[lr].panels){
34050             var r = layout.regions[lr];
34051             var ps = config[lr].panels;
34052             layout.addTypedPanels(r, ps);
34053         }
34054     }
34055     layout.endUpdate();
34056     return layout;
34057 };
34058
34059 // private
34060 Roo.BorderLayout.RegionFactory = {
34061     // private
34062     validRegions : ["north","south","east","west","center"],
34063
34064     // private
34065     create : function(target, mgr, config){
34066         target = target.toLowerCase();
34067         if(config.lightweight || config.basic){
34068             return new Roo.BasicLayoutRegion(mgr, config, target);
34069         }
34070         switch(target){
34071             case "north":
34072                 return new Roo.NorthLayoutRegion(mgr, config);
34073             case "south":
34074                 return new Roo.SouthLayoutRegion(mgr, config);
34075             case "east":
34076                 return new Roo.EastLayoutRegion(mgr, config);
34077             case "west":
34078                 return new Roo.WestLayoutRegion(mgr, config);
34079             case "center":
34080                 return new Roo.CenterLayoutRegion(mgr, config);
34081         }
34082         throw 'Layout region "'+target+'" not supported.';
34083     }
34084 };/*
34085  * Based on:
34086  * Ext JS Library 1.1.1
34087  * Copyright(c) 2006-2007, Ext JS, LLC.
34088  *
34089  * Originally Released Under LGPL - original licence link has changed is not relivant.
34090  *
34091  * Fork - LGPL
34092  * <script type="text/javascript">
34093  */
34094  
34095 /**
34096  * @class Roo.BasicLayoutRegion
34097  * @extends Roo.util.Observable
34098  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34099  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34100  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34101  */
34102 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34103     this.mgr = mgr;
34104     this.position  = pos;
34105     this.events = {
34106         /**
34107          * @scope Roo.BasicLayoutRegion
34108          */
34109         
34110         /**
34111          * @event beforeremove
34112          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34113          * @param {Roo.LayoutRegion} this
34114          * @param {Roo.ContentPanel} panel The panel
34115          * @param {Object} e The cancel event object
34116          */
34117         "beforeremove" : true,
34118         /**
34119          * @event invalidated
34120          * Fires when the layout for this region is changed.
34121          * @param {Roo.LayoutRegion} this
34122          */
34123         "invalidated" : true,
34124         /**
34125          * @event visibilitychange
34126          * Fires when this region is shown or hidden 
34127          * @param {Roo.LayoutRegion} this
34128          * @param {Boolean} visibility true or false
34129          */
34130         "visibilitychange" : true,
34131         /**
34132          * @event paneladded
34133          * Fires when a panel is added. 
34134          * @param {Roo.LayoutRegion} this
34135          * @param {Roo.ContentPanel} panel The panel
34136          */
34137         "paneladded" : true,
34138         /**
34139          * @event panelremoved
34140          * Fires when a panel is removed. 
34141          * @param {Roo.LayoutRegion} this
34142          * @param {Roo.ContentPanel} panel The panel
34143          */
34144         "panelremoved" : true,
34145         /**
34146          * @event beforecollapse
34147          * Fires when this region before collapse.
34148          * @param {Roo.LayoutRegion} this
34149          */
34150         "beforecollapse" : true,
34151         /**
34152          * @event collapsed
34153          * Fires when this region is collapsed.
34154          * @param {Roo.LayoutRegion} this
34155          */
34156         "collapsed" : true,
34157         /**
34158          * @event expanded
34159          * Fires when this region is expanded.
34160          * @param {Roo.LayoutRegion} this
34161          */
34162         "expanded" : true,
34163         /**
34164          * @event slideshow
34165          * Fires when this region is slid into view.
34166          * @param {Roo.LayoutRegion} this
34167          */
34168         "slideshow" : true,
34169         /**
34170          * @event slidehide
34171          * Fires when this region slides out of view. 
34172          * @param {Roo.LayoutRegion} this
34173          */
34174         "slidehide" : true,
34175         /**
34176          * @event panelactivated
34177          * Fires when a panel is activated. 
34178          * @param {Roo.LayoutRegion} this
34179          * @param {Roo.ContentPanel} panel The activated panel
34180          */
34181         "panelactivated" : true,
34182         /**
34183          * @event resized
34184          * Fires when the user resizes this region. 
34185          * @param {Roo.LayoutRegion} this
34186          * @param {Number} newSize The new size (width for east/west, height for north/south)
34187          */
34188         "resized" : true
34189     };
34190     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34191     this.panels = new Roo.util.MixedCollection();
34192     this.panels.getKey = this.getPanelId.createDelegate(this);
34193     this.box = null;
34194     this.activePanel = null;
34195     // ensure listeners are added...
34196     
34197     if (config.listeners || config.events) {
34198         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34199             listeners : config.listeners || {},
34200             events : config.events || {}
34201         });
34202     }
34203     
34204     if(skipConfig !== true){
34205         this.applyConfig(config);
34206     }
34207 };
34208
34209 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34210     getPanelId : function(p){
34211         return p.getId();
34212     },
34213     
34214     applyConfig : function(config){
34215         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34216         this.config = config;
34217         
34218     },
34219     
34220     /**
34221      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34222      * the width, for horizontal (north, south) the height.
34223      * @param {Number} newSize The new width or height
34224      */
34225     resizeTo : function(newSize){
34226         var el = this.el ? this.el :
34227                  (this.activePanel ? this.activePanel.getEl() : null);
34228         if(el){
34229             switch(this.position){
34230                 case "east":
34231                 case "west":
34232                     el.setWidth(newSize);
34233                     this.fireEvent("resized", this, newSize);
34234                 break;
34235                 case "north":
34236                 case "south":
34237                     el.setHeight(newSize);
34238                     this.fireEvent("resized", this, newSize);
34239                 break;                
34240             }
34241         }
34242     },
34243     
34244     getBox : function(){
34245         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34246     },
34247     
34248     getMargins : function(){
34249         return this.margins;
34250     },
34251     
34252     updateBox : function(box){
34253         this.box = box;
34254         var el = this.activePanel.getEl();
34255         el.dom.style.left = box.x + "px";
34256         el.dom.style.top = box.y + "px";
34257         this.activePanel.setSize(box.width, box.height);
34258     },
34259     
34260     /**
34261      * Returns the container element for this region.
34262      * @return {Roo.Element}
34263      */
34264     getEl : function(){
34265         return this.activePanel;
34266     },
34267     
34268     /**
34269      * Returns true if this region is currently visible.
34270      * @return {Boolean}
34271      */
34272     isVisible : function(){
34273         return this.activePanel ? true : false;
34274     },
34275     
34276     setActivePanel : function(panel){
34277         panel = this.getPanel(panel);
34278         if(this.activePanel && this.activePanel != panel){
34279             this.activePanel.setActiveState(false);
34280             this.activePanel.getEl().setLeftTop(-10000,-10000);
34281         }
34282         this.activePanel = panel;
34283         panel.setActiveState(true);
34284         if(this.box){
34285             panel.setSize(this.box.width, this.box.height);
34286         }
34287         this.fireEvent("panelactivated", this, panel);
34288         this.fireEvent("invalidated");
34289     },
34290     
34291     /**
34292      * Show the specified panel.
34293      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34294      * @return {Roo.ContentPanel} The shown panel or null
34295      */
34296     showPanel : function(panel){
34297         if(panel = this.getPanel(panel)){
34298             this.setActivePanel(panel);
34299         }
34300         return panel;
34301     },
34302     
34303     /**
34304      * Get the active panel for this region.
34305      * @return {Roo.ContentPanel} The active panel or null
34306      */
34307     getActivePanel : function(){
34308         return this.activePanel;
34309     },
34310     
34311     /**
34312      * Add the passed ContentPanel(s)
34313      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34314      * @return {Roo.ContentPanel} The panel added (if only one was added)
34315      */
34316     add : function(panel){
34317         if(arguments.length > 1){
34318             for(var i = 0, len = arguments.length; i < len; i++) {
34319                 this.add(arguments[i]);
34320             }
34321             return null;
34322         }
34323         if(this.hasPanel(panel)){
34324             this.showPanel(panel);
34325             return panel;
34326         }
34327         var el = panel.getEl();
34328         if(el.dom.parentNode != this.mgr.el.dom){
34329             this.mgr.el.dom.appendChild(el.dom);
34330         }
34331         if(panel.setRegion){
34332             panel.setRegion(this);
34333         }
34334         this.panels.add(panel);
34335         el.setStyle("position", "absolute");
34336         if(!panel.background){
34337             this.setActivePanel(panel);
34338             if(this.config.initialSize && this.panels.getCount()==1){
34339                 this.resizeTo(this.config.initialSize);
34340             }
34341         }
34342         this.fireEvent("paneladded", this, panel);
34343         return panel;
34344     },
34345     
34346     /**
34347      * Returns true if the panel is in this region.
34348      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34349      * @return {Boolean}
34350      */
34351     hasPanel : function(panel){
34352         if(typeof panel == "object"){ // must be panel obj
34353             panel = panel.getId();
34354         }
34355         return this.getPanel(panel) ? true : false;
34356     },
34357     
34358     /**
34359      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34360      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34361      * @param {Boolean} preservePanel Overrides the config preservePanel option
34362      * @return {Roo.ContentPanel} The panel that was removed
34363      */
34364     remove : function(panel, preservePanel){
34365         panel = this.getPanel(panel);
34366         if(!panel){
34367             return null;
34368         }
34369         var e = {};
34370         this.fireEvent("beforeremove", this, panel, e);
34371         if(e.cancel === true){
34372             return null;
34373         }
34374         var panelId = panel.getId();
34375         this.panels.removeKey(panelId);
34376         return panel;
34377     },
34378     
34379     /**
34380      * Returns the panel specified or null if it's not in this region.
34381      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34382      * @return {Roo.ContentPanel}
34383      */
34384     getPanel : function(id){
34385         if(typeof id == "object"){ // must be panel obj
34386             return id;
34387         }
34388         return this.panels.get(id);
34389     },
34390     
34391     /**
34392      * Returns this regions position (north/south/east/west/center).
34393      * @return {String} 
34394      */
34395     getPosition: function(){
34396         return this.position;    
34397     }
34398 });/*
34399  * Based on:
34400  * Ext JS Library 1.1.1
34401  * Copyright(c) 2006-2007, Ext JS, LLC.
34402  *
34403  * Originally Released Under LGPL - original licence link has changed is not relivant.
34404  *
34405  * Fork - LGPL
34406  * <script type="text/javascript">
34407  */
34408  
34409 /**
34410  * @class Roo.LayoutRegion
34411  * @extends Roo.BasicLayoutRegion
34412  * This class represents a region in a layout manager.
34413  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
34414  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
34415  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
34416  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34417  * @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})
34418  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34419  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
34420  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34421  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34422  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34423  * @cfg {String}    title           The title for the region (overrides panel titles)
34424  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34425  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34426  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34427  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34428  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34429  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34430  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34431  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34432  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34433  * @cfg {Boolean}   showPin         True to show a pin button
34434  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34435  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34436  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34437  * @cfg {Number}    width           For East/West panels
34438  * @cfg {Number}    height          For North/South panels
34439  * @cfg {Boolean}   split           To show the splitter
34440  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34441  */
34442 Roo.LayoutRegion = function(mgr, config, pos){
34443     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34444     var dh = Roo.DomHelper;
34445     /** This region's container element 
34446     * @type Roo.Element */
34447     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34448     /** This region's title element 
34449     * @type Roo.Element */
34450
34451     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34452         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34453         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34454     ]}, true);
34455     this.titleEl.enableDisplayMode();
34456     /** This region's title text element 
34457     * @type HTMLElement */
34458     this.titleTextEl = this.titleEl.dom.firstChild;
34459     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34460     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34461     this.closeBtn.enableDisplayMode();
34462     this.closeBtn.on("click", this.closeClicked, this);
34463     this.closeBtn.hide();
34464
34465     this.createBody(config);
34466     this.visible = true;
34467     this.collapsed = false;
34468
34469     if(config.hideWhenEmpty){
34470         this.hide();
34471         this.on("paneladded", this.validateVisibility, this);
34472         this.on("panelremoved", this.validateVisibility, this);
34473     }
34474     this.applyConfig(config);
34475 };
34476
34477 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34478
34479     createBody : function(){
34480         /** This region's body element 
34481         * @type Roo.Element */
34482         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34483     },
34484
34485     applyConfig : function(c){
34486         if(c.collapsible && this.position != "center" && !this.collapsedEl){
34487             var dh = Roo.DomHelper;
34488             if(c.titlebar !== false){
34489                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34490                 this.collapseBtn.on("click", this.collapse, this);
34491                 this.collapseBtn.enableDisplayMode();
34492
34493                 if(c.showPin === true || this.showPin){
34494                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34495                     this.stickBtn.enableDisplayMode();
34496                     this.stickBtn.on("click", this.expand, this);
34497                     this.stickBtn.hide();
34498                 }
34499             }
34500             /** This region's collapsed element
34501             * @type Roo.Element */
34502             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34503                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34504             ]}, true);
34505             if(c.floatable !== false){
34506                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34507                this.collapsedEl.on("click", this.collapseClick, this);
34508             }
34509
34510             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34511                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34512                    id: "message", unselectable: "on", style:{"float":"left"}});
34513                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34514              }
34515             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34516             this.expandBtn.on("click", this.expand, this);
34517         }
34518         if(this.collapseBtn){
34519             this.collapseBtn.setVisible(c.collapsible == true);
34520         }
34521         this.cmargins = c.cmargins || this.cmargins ||
34522                          (this.position == "west" || this.position == "east" ?
34523                              {top: 0, left: 2, right:2, bottom: 0} :
34524                              {top: 2, left: 0, right:0, bottom: 2});
34525         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34526         this.bottomTabs = c.tabPosition != "top";
34527         this.autoScroll = c.autoScroll || false;
34528         if(this.autoScroll){
34529             this.bodyEl.setStyle("overflow", "auto");
34530         }else{
34531             this.bodyEl.setStyle("overflow", "hidden");
34532         }
34533         //if(c.titlebar !== false){
34534             if((!c.titlebar && !c.title) || c.titlebar === false){
34535                 this.titleEl.hide();
34536             }else{
34537                 this.titleEl.show();
34538                 if(c.title){
34539                     this.titleTextEl.innerHTML = c.title;
34540                 }
34541             }
34542         //}
34543         this.duration = c.duration || .30;
34544         this.slideDuration = c.slideDuration || .45;
34545         this.config = c;
34546         if(c.collapsed){
34547             this.collapse(true);
34548         }
34549         if(c.hidden){
34550             this.hide();
34551         }
34552     },
34553     /**
34554      * Returns true if this region is currently visible.
34555      * @return {Boolean}
34556      */
34557     isVisible : function(){
34558         return this.visible;
34559     },
34560
34561     /**
34562      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34563      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34564      */
34565     setCollapsedTitle : function(title){
34566         title = title || "&#160;";
34567         if(this.collapsedTitleTextEl){
34568             this.collapsedTitleTextEl.innerHTML = title;
34569         }
34570     },
34571
34572     getBox : function(){
34573         var b;
34574         if(!this.collapsed){
34575             b = this.el.getBox(false, true);
34576         }else{
34577             b = this.collapsedEl.getBox(false, true);
34578         }
34579         return b;
34580     },
34581
34582     getMargins : function(){
34583         return this.collapsed ? this.cmargins : this.margins;
34584     },
34585
34586     highlight : function(){
34587         this.el.addClass("x-layout-panel-dragover");
34588     },
34589
34590     unhighlight : function(){
34591         this.el.removeClass("x-layout-panel-dragover");
34592     },
34593
34594     updateBox : function(box){
34595         this.box = box;
34596         if(!this.collapsed){
34597             this.el.dom.style.left = box.x + "px";
34598             this.el.dom.style.top = box.y + "px";
34599             this.updateBody(box.width, box.height);
34600         }else{
34601             this.collapsedEl.dom.style.left = box.x + "px";
34602             this.collapsedEl.dom.style.top = box.y + "px";
34603             this.collapsedEl.setSize(box.width, box.height);
34604         }
34605         if(this.tabs){
34606             this.tabs.autoSizeTabs();
34607         }
34608     },
34609
34610     updateBody : function(w, h){
34611         if(w !== null){
34612             this.el.setWidth(w);
34613             w -= this.el.getBorderWidth("rl");
34614             if(this.config.adjustments){
34615                 w += this.config.adjustments[0];
34616             }
34617         }
34618         if(h !== null){
34619             this.el.setHeight(h);
34620             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34621             h -= this.el.getBorderWidth("tb");
34622             if(this.config.adjustments){
34623                 h += this.config.adjustments[1];
34624             }
34625             this.bodyEl.setHeight(h);
34626             if(this.tabs){
34627                 h = this.tabs.syncHeight(h);
34628             }
34629         }
34630         if(this.panelSize){
34631             w = w !== null ? w : this.panelSize.width;
34632             h = h !== null ? h : this.panelSize.height;
34633         }
34634         if(this.activePanel){
34635             var el = this.activePanel.getEl();
34636             w = w !== null ? w : el.getWidth();
34637             h = h !== null ? h : el.getHeight();
34638             this.panelSize = {width: w, height: h};
34639             this.activePanel.setSize(w, h);
34640         }
34641         if(Roo.isIE && this.tabs){
34642             this.tabs.el.repaint();
34643         }
34644     },
34645
34646     /**
34647      * Returns the container element for this region.
34648      * @return {Roo.Element}
34649      */
34650     getEl : function(){
34651         return this.el;
34652     },
34653
34654     /**
34655      * Hides this region.
34656      */
34657     hide : function(){
34658         if(!this.collapsed){
34659             this.el.dom.style.left = "-2000px";
34660             this.el.hide();
34661         }else{
34662             this.collapsedEl.dom.style.left = "-2000px";
34663             this.collapsedEl.hide();
34664         }
34665         this.visible = false;
34666         this.fireEvent("visibilitychange", this, false);
34667     },
34668
34669     /**
34670      * Shows this region if it was previously hidden.
34671      */
34672     show : function(){
34673         if(!this.collapsed){
34674             this.el.show();
34675         }else{
34676             this.collapsedEl.show();
34677         }
34678         this.visible = true;
34679         this.fireEvent("visibilitychange", this, true);
34680     },
34681
34682     closeClicked : function(){
34683         if(this.activePanel){
34684             this.remove(this.activePanel);
34685         }
34686     },
34687
34688     collapseClick : function(e){
34689         if(this.isSlid){
34690            e.stopPropagation();
34691            this.slideIn();
34692         }else{
34693            e.stopPropagation();
34694            this.slideOut();
34695         }
34696     },
34697
34698     /**
34699      * Collapses this region.
34700      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34701      */
34702     collapse : function(skipAnim, skipCheck){
34703         if(this.collapsed) {
34704             return;
34705         }
34706         
34707         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34708             
34709             this.collapsed = true;
34710             if(this.split){
34711                 this.split.el.hide();
34712             }
34713             if(this.config.animate && skipAnim !== true){
34714                 this.fireEvent("invalidated", this);
34715                 this.animateCollapse();
34716             }else{
34717                 this.el.setLocation(-20000,-20000);
34718                 this.el.hide();
34719                 this.collapsedEl.show();
34720                 this.fireEvent("collapsed", this);
34721                 this.fireEvent("invalidated", this);
34722             }
34723         }
34724         
34725     },
34726
34727     animateCollapse : function(){
34728         // overridden
34729     },
34730
34731     /**
34732      * Expands this region if it was previously collapsed.
34733      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34734      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34735      */
34736     expand : function(e, skipAnim){
34737         if(e) {
34738             e.stopPropagation();
34739         }
34740         if(!this.collapsed || this.el.hasActiveFx()) {
34741             return;
34742         }
34743         if(this.isSlid){
34744             this.afterSlideIn();
34745             skipAnim = true;
34746         }
34747         this.collapsed = false;
34748         if(this.config.animate && skipAnim !== true){
34749             this.animateExpand();
34750         }else{
34751             this.el.show();
34752             if(this.split){
34753                 this.split.el.show();
34754             }
34755             this.collapsedEl.setLocation(-2000,-2000);
34756             this.collapsedEl.hide();
34757             this.fireEvent("invalidated", this);
34758             this.fireEvent("expanded", this);
34759         }
34760     },
34761
34762     animateExpand : function(){
34763         // overridden
34764     },
34765
34766     initTabs : function()
34767     {
34768         this.bodyEl.setStyle("overflow", "hidden");
34769         var ts = new Roo.TabPanel(
34770                 this.bodyEl.dom,
34771                 {
34772                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34773                     disableTooltips: this.config.disableTabTips,
34774                     toolbar : this.config.toolbar
34775                 }
34776         );
34777         if(this.config.hideTabs){
34778             ts.stripWrap.setDisplayed(false);
34779         }
34780         this.tabs = ts;
34781         ts.resizeTabs = this.config.resizeTabs === true;
34782         ts.minTabWidth = this.config.minTabWidth || 40;
34783         ts.maxTabWidth = this.config.maxTabWidth || 250;
34784         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34785         ts.monitorResize = false;
34786         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34787         ts.bodyEl.addClass('x-layout-tabs-body');
34788         this.panels.each(this.initPanelAsTab, this);
34789     },
34790
34791     initPanelAsTab : function(panel){
34792         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34793                     this.config.closeOnTab && panel.isClosable());
34794         if(panel.tabTip !== undefined){
34795             ti.setTooltip(panel.tabTip);
34796         }
34797         ti.on("activate", function(){
34798               this.setActivePanel(panel);
34799         }, this);
34800         if(this.config.closeOnTab){
34801             ti.on("beforeclose", function(t, e){
34802                 e.cancel = true;
34803                 this.remove(panel);
34804             }, this);
34805         }
34806         return ti;
34807     },
34808
34809     updatePanelTitle : function(panel, title){
34810         if(this.activePanel == panel){
34811             this.updateTitle(title);
34812         }
34813         if(this.tabs){
34814             var ti = this.tabs.getTab(panel.getEl().id);
34815             ti.setText(title);
34816             if(panel.tabTip !== undefined){
34817                 ti.setTooltip(panel.tabTip);
34818             }
34819         }
34820     },
34821
34822     updateTitle : function(title){
34823         if(this.titleTextEl && !this.config.title){
34824             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34825         }
34826     },
34827
34828     setActivePanel : function(panel){
34829         panel = this.getPanel(panel);
34830         if(this.activePanel && this.activePanel != panel){
34831             this.activePanel.setActiveState(false);
34832         }
34833         this.activePanel = panel;
34834         panel.setActiveState(true);
34835         if(this.panelSize){
34836             panel.setSize(this.panelSize.width, this.panelSize.height);
34837         }
34838         if(this.closeBtn){
34839             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34840         }
34841         this.updateTitle(panel.getTitle());
34842         if(this.tabs){
34843             this.fireEvent("invalidated", this);
34844         }
34845         this.fireEvent("panelactivated", this, panel);
34846     },
34847
34848     /**
34849      * Shows the specified panel.
34850      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34851      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34852      */
34853     showPanel : function(panel)
34854     {
34855         panel = this.getPanel(panel);
34856         if(panel){
34857             if(this.tabs){
34858                 var tab = this.tabs.getTab(panel.getEl().id);
34859                 if(tab.isHidden()){
34860                     this.tabs.unhideTab(tab.id);
34861                 }
34862                 tab.activate();
34863             }else{
34864                 this.setActivePanel(panel);
34865             }
34866         }
34867         return panel;
34868     },
34869
34870     /**
34871      * Get the active panel for this region.
34872      * @return {Roo.ContentPanel} The active panel or null
34873      */
34874     getActivePanel : function(){
34875         return this.activePanel;
34876     },
34877
34878     validateVisibility : function(){
34879         if(this.panels.getCount() < 1){
34880             this.updateTitle("&#160;");
34881             this.closeBtn.hide();
34882             this.hide();
34883         }else{
34884             if(!this.isVisible()){
34885                 this.show();
34886             }
34887         }
34888     },
34889
34890     /**
34891      * Adds the passed ContentPanel(s) to this region.
34892      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34893      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34894      */
34895     add : function(panel){
34896         if(arguments.length > 1){
34897             for(var i = 0, len = arguments.length; i < len; i++) {
34898                 this.add(arguments[i]);
34899             }
34900             return null;
34901         }
34902         if(this.hasPanel(panel)){
34903             this.showPanel(panel);
34904             return panel;
34905         }
34906         panel.setRegion(this);
34907         this.panels.add(panel);
34908         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34909             this.bodyEl.dom.appendChild(panel.getEl().dom);
34910             if(panel.background !== true){
34911                 this.setActivePanel(panel);
34912             }
34913             this.fireEvent("paneladded", this, panel);
34914             return panel;
34915         }
34916         if(!this.tabs){
34917             this.initTabs();
34918         }else{
34919             this.initPanelAsTab(panel);
34920         }
34921         if(panel.background !== true){
34922             this.tabs.activate(panel.getEl().id);
34923         }
34924         this.fireEvent("paneladded", this, panel);
34925         return panel;
34926     },
34927
34928     /**
34929      * Hides the tab for the specified panel.
34930      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34931      */
34932     hidePanel : function(panel){
34933         if(this.tabs && (panel = this.getPanel(panel))){
34934             this.tabs.hideTab(panel.getEl().id);
34935         }
34936     },
34937
34938     /**
34939      * Unhides the tab for a previously hidden panel.
34940      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34941      */
34942     unhidePanel : function(panel){
34943         if(this.tabs && (panel = this.getPanel(panel))){
34944             this.tabs.unhideTab(panel.getEl().id);
34945         }
34946     },
34947
34948     clearPanels : function(){
34949         while(this.panels.getCount() > 0){
34950              this.remove(this.panels.first());
34951         }
34952     },
34953
34954     /**
34955      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34956      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34957      * @param {Boolean} preservePanel Overrides the config preservePanel option
34958      * @return {Roo.ContentPanel} The panel that was removed
34959      */
34960     remove : function(panel, preservePanel){
34961         panel = this.getPanel(panel);
34962         if(!panel){
34963             return null;
34964         }
34965         var e = {};
34966         this.fireEvent("beforeremove", this, panel, e);
34967         if(e.cancel === true){
34968             return null;
34969         }
34970         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34971         var panelId = panel.getId();
34972         this.panels.removeKey(panelId);
34973         if(preservePanel){
34974             document.body.appendChild(panel.getEl().dom);
34975         }
34976         if(this.tabs){
34977             this.tabs.removeTab(panel.getEl().id);
34978         }else if (!preservePanel){
34979             this.bodyEl.dom.removeChild(panel.getEl().dom);
34980         }
34981         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34982             var p = this.panels.first();
34983             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34984             tempEl.appendChild(p.getEl().dom);
34985             this.bodyEl.update("");
34986             this.bodyEl.dom.appendChild(p.getEl().dom);
34987             tempEl = null;
34988             this.updateTitle(p.getTitle());
34989             this.tabs = null;
34990             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34991             this.setActivePanel(p);
34992         }
34993         panel.setRegion(null);
34994         if(this.activePanel == panel){
34995             this.activePanel = null;
34996         }
34997         if(this.config.autoDestroy !== false && preservePanel !== true){
34998             try{panel.destroy();}catch(e){}
34999         }
35000         this.fireEvent("panelremoved", this, panel);
35001         return panel;
35002     },
35003
35004     /**
35005      * Returns the TabPanel component used by this region
35006      * @return {Roo.TabPanel}
35007      */
35008     getTabs : function(){
35009         return this.tabs;
35010     },
35011
35012     createTool : function(parentEl, className){
35013         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35014             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
35015         btn.addClassOnOver("x-layout-tools-button-over");
35016         return btn;
35017     }
35018 });/*
35019  * Based on:
35020  * Ext JS Library 1.1.1
35021  * Copyright(c) 2006-2007, Ext JS, LLC.
35022  *
35023  * Originally Released Under LGPL - original licence link has changed is not relivant.
35024  *
35025  * Fork - LGPL
35026  * <script type="text/javascript">
35027  */
35028  
35029
35030
35031 /**
35032  * @class Roo.SplitLayoutRegion
35033  * @extends Roo.LayoutRegion
35034  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35035  */
35036 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35037     this.cursor = cursor;
35038     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35039 };
35040
35041 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35042     splitTip : "Drag to resize.",
35043     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35044     useSplitTips : false,
35045
35046     applyConfig : function(config){
35047         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35048         if(config.split){
35049             if(!this.split){
35050                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
35051                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
35052                 /** The SplitBar for this region 
35053                 * @type Roo.SplitBar */
35054                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35055                 this.split.on("moved", this.onSplitMove, this);
35056                 this.split.useShim = config.useShim === true;
35057                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35058                 if(this.useSplitTips){
35059                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35060                 }
35061                 if(config.collapsible){
35062                     this.split.el.on("dblclick", this.collapse,  this);
35063                 }
35064             }
35065             if(typeof config.minSize != "undefined"){
35066                 this.split.minSize = config.minSize;
35067             }
35068             if(typeof config.maxSize != "undefined"){
35069                 this.split.maxSize = config.maxSize;
35070             }
35071             if(config.hideWhenEmpty || config.hidden || config.collapsed){
35072                 this.hideSplitter();
35073             }
35074         }
35075     },
35076
35077     getHMaxSize : function(){
35078          var cmax = this.config.maxSize || 10000;
35079          var center = this.mgr.getRegion("center");
35080          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35081     },
35082
35083     getVMaxSize : function(){
35084          var cmax = this.config.maxSize || 10000;
35085          var center = this.mgr.getRegion("center");
35086          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35087     },
35088
35089     onSplitMove : function(split, newSize){
35090         this.fireEvent("resized", this, newSize);
35091     },
35092     
35093     /** 
35094      * Returns the {@link Roo.SplitBar} for this region.
35095      * @return {Roo.SplitBar}
35096      */
35097     getSplitBar : function(){
35098         return this.split;
35099     },
35100     
35101     hide : function(){
35102         this.hideSplitter();
35103         Roo.SplitLayoutRegion.superclass.hide.call(this);
35104     },
35105
35106     hideSplitter : function(){
35107         if(this.split){
35108             this.split.el.setLocation(-2000,-2000);
35109             this.split.el.hide();
35110         }
35111     },
35112
35113     show : function(){
35114         if(this.split){
35115             this.split.el.show();
35116         }
35117         Roo.SplitLayoutRegion.superclass.show.call(this);
35118     },
35119     
35120     beforeSlide: function(){
35121         if(Roo.isGecko){// firefox overflow auto bug workaround
35122             this.bodyEl.clip();
35123             if(this.tabs) {
35124                 this.tabs.bodyEl.clip();
35125             }
35126             if(this.activePanel){
35127                 this.activePanel.getEl().clip();
35128                 
35129                 if(this.activePanel.beforeSlide){
35130                     this.activePanel.beforeSlide();
35131                 }
35132             }
35133         }
35134     },
35135     
35136     afterSlide : function(){
35137         if(Roo.isGecko){// firefox overflow auto bug workaround
35138             this.bodyEl.unclip();
35139             if(this.tabs) {
35140                 this.tabs.bodyEl.unclip();
35141             }
35142             if(this.activePanel){
35143                 this.activePanel.getEl().unclip();
35144                 if(this.activePanel.afterSlide){
35145                     this.activePanel.afterSlide();
35146                 }
35147             }
35148         }
35149     },
35150
35151     initAutoHide : function(){
35152         if(this.autoHide !== false){
35153             if(!this.autoHideHd){
35154                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35155                 this.autoHideHd = {
35156                     "mouseout": function(e){
35157                         if(!e.within(this.el, true)){
35158                             st.delay(500);
35159                         }
35160                     },
35161                     "mouseover" : function(e){
35162                         st.cancel();
35163                     },
35164                     scope : this
35165                 };
35166             }
35167             this.el.on(this.autoHideHd);
35168         }
35169     },
35170
35171     clearAutoHide : function(){
35172         if(this.autoHide !== false){
35173             this.el.un("mouseout", this.autoHideHd.mouseout);
35174             this.el.un("mouseover", this.autoHideHd.mouseover);
35175         }
35176     },
35177
35178     clearMonitor : function(){
35179         Roo.get(document).un("click", this.slideInIf, this);
35180     },
35181
35182     // these names are backwards but not changed for compat
35183     slideOut : function(){
35184         if(this.isSlid || this.el.hasActiveFx()){
35185             return;
35186         }
35187         this.isSlid = true;
35188         if(this.collapseBtn){
35189             this.collapseBtn.hide();
35190         }
35191         this.closeBtnState = this.closeBtn.getStyle('display');
35192         this.closeBtn.hide();
35193         if(this.stickBtn){
35194             this.stickBtn.show();
35195         }
35196         this.el.show();
35197         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35198         this.beforeSlide();
35199         this.el.setStyle("z-index", 10001);
35200         this.el.slideIn(this.getSlideAnchor(), {
35201             callback: function(){
35202                 this.afterSlide();
35203                 this.initAutoHide();
35204                 Roo.get(document).on("click", this.slideInIf, this);
35205                 this.fireEvent("slideshow", this);
35206             },
35207             scope: this,
35208             block: true
35209         });
35210     },
35211
35212     afterSlideIn : function(){
35213         this.clearAutoHide();
35214         this.isSlid = false;
35215         this.clearMonitor();
35216         this.el.setStyle("z-index", "");
35217         if(this.collapseBtn){
35218             this.collapseBtn.show();
35219         }
35220         this.closeBtn.setStyle('display', this.closeBtnState);
35221         if(this.stickBtn){
35222             this.stickBtn.hide();
35223         }
35224         this.fireEvent("slidehide", this);
35225     },
35226
35227     slideIn : function(cb){
35228         if(!this.isSlid || this.el.hasActiveFx()){
35229             Roo.callback(cb);
35230             return;
35231         }
35232         this.isSlid = false;
35233         this.beforeSlide();
35234         this.el.slideOut(this.getSlideAnchor(), {
35235             callback: function(){
35236                 this.el.setLeftTop(-10000, -10000);
35237                 this.afterSlide();
35238                 this.afterSlideIn();
35239                 Roo.callback(cb);
35240             },
35241             scope: this,
35242             block: true
35243         });
35244     },
35245     
35246     slideInIf : function(e){
35247         if(!e.within(this.el)){
35248             this.slideIn();
35249         }
35250     },
35251
35252     animateCollapse : function(){
35253         this.beforeSlide();
35254         this.el.setStyle("z-index", 20000);
35255         var anchor = this.getSlideAnchor();
35256         this.el.slideOut(anchor, {
35257             callback : function(){
35258                 this.el.setStyle("z-index", "");
35259                 this.collapsedEl.slideIn(anchor, {duration:.3});
35260                 this.afterSlide();
35261                 this.el.setLocation(-10000,-10000);
35262                 this.el.hide();
35263                 this.fireEvent("collapsed", this);
35264             },
35265             scope: this,
35266             block: true
35267         });
35268     },
35269
35270     animateExpand : function(){
35271         this.beforeSlide();
35272         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35273         this.el.setStyle("z-index", 20000);
35274         this.collapsedEl.hide({
35275             duration:.1
35276         });
35277         this.el.slideIn(this.getSlideAnchor(), {
35278             callback : function(){
35279                 this.el.setStyle("z-index", "");
35280                 this.afterSlide();
35281                 if(this.split){
35282                     this.split.el.show();
35283                 }
35284                 this.fireEvent("invalidated", this);
35285                 this.fireEvent("expanded", this);
35286             },
35287             scope: this,
35288             block: true
35289         });
35290     },
35291
35292     anchors : {
35293         "west" : "left",
35294         "east" : "right",
35295         "north" : "top",
35296         "south" : "bottom"
35297     },
35298
35299     sanchors : {
35300         "west" : "l",
35301         "east" : "r",
35302         "north" : "t",
35303         "south" : "b"
35304     },
35305
35306     canchors : {
35307         "west" : "tl-tr",
35308         "east" : "tr-tl",
35309         "north" : "tl-bl",
35310         "south" : "bl-tl"
35311     },
35312
35313     getAnchor : function(){
35314         return this.anchors[this.position];
35315     },
35316
35317     getCollapseAnchor : function(){
35318         return this.canchors[this.position];
35319     },
35320
35321     getSlideAnchor : function(){
35322         return this.sanchors[this.position];
35323     },
35324
35325     getAlignAdj : function(){
35326         var cm = this.cmargins;
35327         switch(this.position){
35328             case "west":
35329                 return [0, 0];
35330             break;
35331             case "east":
35332                 return [0, 0];
35333             break;
35334             case "north":
35335                 return [0, 0];
35336             break;
35337             case "south":
35338                 return [0, 0];
35339             break;
35340         }
35341     },
35342
35343     getExpandAdj : function(){
35344         var c = this.collapsedEl, cm = this.cmargins;
35345         switch(this.position){
35346             case "west":
35347                 return [-(cm.right+c.getWidth()+cm.left), 0];
35348             break;
35349             case "east":
35350                 return [cm.right+c.getWidth()+cm.left, 0];
35351             break;
35352             case "north":
35353                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35354             break;
35355             case "south":
35356                 return [0, cm.top+cm.bottom+c.getHeight()];
35357             break;
35358         }
35359     }
35360 });/*
35361  * Based on:
35362  * Ext JS Library 1.1.1
35363  * Copyright(c) 2006-2007, Ext JS, LLC.
35364  *
35365  * Originally Released Under LGPL - original licence link has changed is not relivant.
35366  *
35367  * Fork - LGPL
35368  * <script type="text/javascript">
35369  */
35370 /*
35371  * These classes are private internal classes
35372  */
35373 Roo.CenterLayoutRegion = function(mgr, config){
35374     Roo.LayoutRegion.call(this, mgr, config, "center");
35375     this.visible = true;
35376     this.minWidth = config.minWidth || 20;
35377     this.minHeight = config.minHeight || 20;
35378 };
35379
35380 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35381     hide : function(){
35382         // center panel can't be hidden
35383     },
35384     
35385     show : function(){
35386         // center panel can't be hidden
35387     },
35388     
35389     getMinWidth: function(){
35390         return this.minWidth;
35391     },
35392     
35393     getMinHeight: function(){
35394         return this.minHeight;
35395     }
35396 });
35397
35398
35399 Roo.NorthLayoutRegion = function(mgr, config){
35400     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35401     if(this.split){
35402         this.split.placement = Roo.SplitBar.TOP;
35403         this.split.orientation = Roo.SplitBar.VERTICAL;
35404         this.split.el.addClass("x-layout-split-v");
35405     }
35406     var size = config.initialSize || config.height;
35407     if(typeof size != "undefined"){
35408         this.el.setHeight(size);
35409     }
35410 };
35411 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35412     orientation: Roo.SplitBar.VERTICAL,
35413     getBox : function(){
35414         if(this.collapsed){
35415             return this.collapsedEl.getBox();
35416         }
35417         var box = this.el.getBox();
35418         if(this.split){
35419             box.height += this.split.el.getHeight();
35420         }
35421         return box;
35422     },
35423     
35424     updateBox : function(box){
35425         if(this.split && !this.collapsed){
35426             box.height -= this.split.el.getHeight();
35427             this.split.el.setLeft(box.x);
35428             this.split.el.setTop(box.y+box.height);
35429             this.split.el.setWidth(box.width);
35430         }
35431         if(this.collapsed){
35432             this.updateBody(box.width, null);
35433         }
35434         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35435     }
35436 });
35437
35438 Roo.SouthLayoutRegion = function(mgr, config){
35439     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35440     if(this.split){
35441         this.split.placement = Roo.SplitBar.BOTTOM;
35442         this.split.orientation = Roo.SplitBar.VERTICAL;
35443         this.split.el.addClass("x-layout-split-v");
35444     }
35445     var size = config.initialSize || config.height;
35446     if(typeof size != "undefined"){
35447         this.el.setHeight(size);
35448     }
35449 };
35450 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35451     orientation: Roo.SplitBar.VERTICAL,
35452     getBox : function(){
35453         if(this.collapsed){
35454             return this.collapsedEl.getBox();
35455         }
35456         var box = this.el.getBox();
35457         if(this.split){
35458             var sh = this.split.el.getHeight();
35459             box.height += sh;
35460             box.y -= sh;
35461         }
35462         return box;
35463     },
35464     
35465     updateBox : function(box){
35466         if(this.split && !this.collapsed){
35467             var sh = this.split.el.getHeight();
35468             box.height -= sh;
35469             box.y += sh;
35470             this.split.el.setLeft(box.x);
35471             this.split.el.setTop(box.y-sh);
35472             this.split.el.setWidth(box.width);
35473         }
35474         if(this.collapsed){
35475             this.updateBody(box.width, null);
35476         }
35477         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35478     }
35479 });
35480
35481 Roo.EastLayoutRegion = function(mgr, config){
35482     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35483     if(this.split){
35484         this.split.placement = Roo.SplitBar.RIGHT;
35485         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35486         this.split.el.addClass("x-layout-split-h");
35487     }
35488     var size = config.initialSize || config.width;
35489     if(typeof size != "undefined"){
35490         this.el.setWidth(size);
35491     }
35492 };
35493 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35494     orientation: Roo.SplitBar.HORIZONTAL,
35495     getBox : function(){
35496         if(this.collapsed){
35497             return this.collapsedEl.getBox();
35498         }
35499         var box = this.el.getBox();
35500         if(this.split){
35501             var sw = this.split.el.getWidth();
35502             box.width += sw;
35503             box.x -= sw;
35504         }
35505         return box;
35506     },
35507
35508     updateBox : function(box){
35509         if(this.split && !this.collapsed){
35510             var sw = this.split.el.getWidth();
35511             box.width -= sw;
35512             this.split.el.setLeft(box.x);
35513             this.split.el.setTop(box.y);
35514             this.split.el.setHeight(box.height);
35515             box.x += sw;
35516         }
35517         if(this.collapsed){
35518             this.updateBody(null, box.height);
35519         }
35520         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35521     }
35522 });
35523
35524 Roo.WestLayoutRegion = function(mgr, config){
35525     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35526     if(this.split){
35527         this.split.placement = Roo.SplitBar.LEFT;
35528         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35529         this.split.el.addClass("x-layout-split-h");
35530     }
35531     var size = config.initialSize || config.width;
35532     if(typeof size != "undefined"){
35533         this.el.setWidth(size);
35534     }
35535 };
35536 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35537     orientation: Roo.SplitBar.HORIZONTAL,
35538     getBox : function(){
35539         if(this.collapsed){
35540             return this.collapsedEl.getBox();
35541         }
35542         var box = this.el.getBox();
35543         if(this.split){
35544             box.width += this.split.el.getWidth();
35545         }
35546         return box;
35547     },
35548     
35549     updateBox : function(box){
35550         if(this.split && !this.collapsed){
35551             var sw = this.split.el.getWidth();
35552             box.width -= sw;
35553             this.split.el.setLeft(box.x+box.width);
35554             this.split.el.setTop(box.y);
35555             this.split.el.setHeight(box.height);
35556         }
35557         if(this.collapsed){
35558             this.updateBody(null, box.height);
35559         }
35560         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35561     }
35562 });
35563 /*
35564  * Based on:
35565  * Ext JS Library 1.1.1
35566  * Copyright(c) 2006-2007, Ext JS, LLC.
35567  *
35568  * Originally Released Under LGPL - original licence link has changed is not relivant.
35569  *
35570  * Fork - LGPL
35571  * <script type="text/javascript">
35572  */
35573  
35574  
35575 /*
35576  * Private internal class for reading and applying state
35577  */
35578 Roo.LayoutStateManager = function(layout){
35579      // default empty state
35580      this.state = {
35581         north: {},
35582         south: {},
35583         east: {},
35584         west: {}       
35585     };
35586 };
35587
35588 Roo.LayoutStateManager.prototype = {
35589     init : function(layout, provider){
35590         this.provider = provider;
35591         var state = provider.get(layout.id+"-layout-state");
35592         if(state){
35593             var wasUpdating = layout.isUpdating();
35594             if(!wasUpdating){
35595                 layout.beginUpdate();
35596             }
35597             for(var key in state){
35598                 if(typeof state[key] != "function"){
35599                     var rstate = state[key];
35600                     var r = layout.getRegion(key);
35601                     if(r && rstate){
35602                         if(rstate.size){
35603                             r.resizeTo(rstate.size);
35604                         }
35605                         if(rstate.collapsed == true){
35606                             r.collapse(true);
35607                         }else{
35608                             r.expand(null, true);
35609                         }
35610                     }
35611                 }
35612             }
35613             if(!wasUpdating){
35614                 layout.endUpdate();
35615             }
35616             this.state = state; 
35617         }
35618         this.layout = layout;
35619         layout.on("regionresized", this.onRegionResized, this);
35620         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35621         layout.on("regionexpanded", this.onRegionExpanded, this);
35622     },
35623     
35624     storeState : function(){
35625         this.provider.set(this.layout.id+"-layout-state", this.state);
35626     },
35627     
35628     onRegionResized : function(region, newSize){
35629         this.state[region.getPosition()].size = newSize;
35630         this.storeState();
35631     },
35632     
35633     onRegionCollapsed : function(region){
35634         this.state[region.getPosition()].collapsed = true;
35635         this.storeState();
35636     },
35637     
35638     onRegionExpanded : function(region){
35639         this.state[region.getPosition()].collapsed = false;
35640         this.storeState();
35641     }
35642 };/*
35643  * Based on:
35644  * Ext JS Library 1.1.1
35645  * Copyright(c) 2006-2007, Ext JS, LLC.
35646  *
35647  * Originally Released Under LGPL - original licence link has changed is not relivant.
35648  *
35649  * Fork - LGPL
35650  * <script type="text/javascript">
35651  */
35652 /**
35653  * @class Roo.ContentPanel
35654  * @extends Roo.util.Observable
35655  * @children Roo.form.Form Roo.JsonView Roo.View
35656  * @parent Roo.BorderLayout Roo.LayoutDialog builder
35657  * A basic ContentPanel element.
35658  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35659  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35660  * @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
35661  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35662  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35663  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35664  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
35665  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35666  * @cfg {String} title          The title for this panel
35667  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35668  * @cfg {String} url            Calls {@link #setUrl} with this value
35669  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35670  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35671  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35672  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35673  * @cfg {String}    style  Extra style to add to the content panel
35674  * @cfg {Roo.menu.Menu} menu  popup menu
35675
35676  * @constructor
35677  * Create a new ContentPanel.
35678  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35679  * @param {String/Object} config A string to set only the title or a config object
35680  * @param {String} content (optional) Set the HTML content for this panel
35681  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35682  */
35683 Roo.ContentPanel = function(el, config, content){
35684     
35685      
35686     /*
35687     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35688         config = el;
35689         el = Roo.id();
35690     }
35691     if (config && config.parentLayout) { 
35692         el = config.parentLayout.el.createChild(); 
35693     }
35694     */
35695     if(el.autoCreate){ // xtype is available if this is called from factory
35696         config = el;
35697         el = Roo.id();
35698     }
35699     this.el = Roo.get(el);
35700     if(!this.el && config && config.autoCreate){
35701         if(typeof config.autoCreate == "object"){
35702             if(!config.autoCreate.id){
35703                 config.autoCreate.id = config.id||el;
35704             }
35705             this.el = Roo.DomHelper.append(document.body,
35706                         config.autoCreate, true);
35707         }else{
35708             this.el = Roo.DomHelper.append(document.body,
35709                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35710         }
35711     }
35712     
35713     
35714     this.closable = false;
35715     this.loaded = false;
35716     this.active = false;
35717     if(typeof config == "string"){
35718         this.title = config;
35719     }else{
35720         Roo.apply(this, config);
35721     }
35722     
35723     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35724         this.wrapEl = this.el.wrap();
35725         this.toolbar.container = this.el.insertSibling(false, 'before');
35726         this.toolbar = new Roo.Toolbar(this.toolbar);
35727     }
35728     
35729     // xtype created footer. - not sure if will work as we normally have to render first..
35730     if (this.footer && !this.footer.el && this.footer.xtype) {
35731         if (!this.wrapEl) {
35732             this.wrapEl = this.el.wrap();
35733         }
35734     
35735         this.footer.container = this.wrapEl.createChild();
35736          
35737         this.footer = Roo.factory(this.footer, Roo);
35738         
35739     }
35740     
35741     if(this.resizeEl){
35742         this.resizeEl = Roo.get(this.resizeEl, true);
35743     }else{
35744         this.resizeEl = this.el;
35745     }
35746     // handle view.xtype
35747     
35748  
35749     
35750     
35751     this.addEvents({
35752         /**
35753          * @event activate
35754          * Fires when this panel is activated. 
35755          * @param {Roo.ContentPanel} this
35756          */
35757         "activate" : true,
35758         /**
35759          * @event deactivate
35760          * Fires when this panel is activated. 
35761          * @param {Roo.ContentPanel} this
35762          */
35763         "deactivate" : true,
35764
35765         /**
35766          * @event resize
35767          * Fires when this panel is resized if fitToFrame is true.
35768          * @param {Roo.ContentPanel} this
35769          * @param {Number} width The width after any component adjustments
35770          * @param {Number} height The height after any component adjustments
35771          */
35772         "resize" : true,
35773         
35774          /**
35775          * @event render
35776          * Fires when this tab is created
35777          * @param {Roo.ContentPanel} this
35778          */
35779         "render" : true
35780          
35781         
35782     });
35783     
35784
35785     
35786     
35787     if(this.autoScroll){
35788         this.resizeEl.setStyle("overflow", "auto");
35789     } else {
35790         // fix randome scrolling
35791         this.el.on('scroll', function() {
35792             Roo.log('fix random scolling');
35793             this.scrollTo('top',0); 
35794         });
35795     }
35796     content = content || this.content;
35797     if(content){
35798         this.setContent(content);
35799     }
35800     if(config && config.url){
35801         this.setUrl(this.url, this.params, this.loadOnce);
35802     }
35803     
35804     
35805     
35806     Roo.ContentPanel.superclass.constructor.call(this);
35807     
35808     if (this.view && typeof(this.view.xtype) != 'undefined') {
35809         this.view.el = this.el.appendChild(document.createElement("div"));
35810         this.view = Roo.factory(this.view); 
35811         this.view.render  &&  this.view.render(false, '');  
35812     }
35813     
35814     
35815     this.fireEvent('render', this);
35816 };
35817
35818 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35819     tabTip:'',
35820     setRegion : function(region){
35821         this.region = region;
35822         if(region){
35823            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35824         }else{
35825            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35826         } 
35827     },
35828     
35829     /**
35830      * Returns the toolbar for this Panel if one was configured. 
35831      * @return {Roo.Toolbar} 
35832      */
35833     getToolbar : function(){
35834         return this.toolbar;
35835     },
35836     
35837     setActiveState : function(active){
35838         this.active = active;
35839         if(!active){
35840             this.fireEvent("deactivate", this);
35841         }else{
35842             this.fireEvent("activate", this);
35843         }
35844     },
35845     /**
35846      * Updates this panel's element
35847      * @param {String} content The new content
35848      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35849     */
35850     setContent : function(content, loadScripts){
35851         this.el.update(content, loadScripts);
35852     },
35853
35854     ignoreResize : function(w, h){
35855         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35856             return true;
35857         }else{
35858             this.lastSize = {width: w, height: h};
35859             return false;
35860         }
35861     },
35862     /**
35863      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35864      * @return {Roo.UpdateManager} The UpdateManager
35865      */
35866     getUpdateManager : function(){
35867         return this.el.getUpdateManager();
35868     },
35869      /**
35870      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35871      * @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:
35872 <pre><code>
35873 panel.load({
35874     url: "your-url.php",
35875     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35876     callback: yourFunction,
35877     scope: yourObject, //(optional scope)
35878     discardUrl: false,
35879     nocache: false,
35880     text: "Loading...",
35881     timeout: 30,
35882     scripts: false
35883 });
35884 </code></pre>
35885      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35886      * 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.
35887      * @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}
35888      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35889      * @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.
35890      * @return {Roo.ContentPanel} this
35891      */
35892     load : function(){
35893         var um = this.el.getUpdateManager();
35894         um.update.apply(um, arguments);
35895         return this;
35896     },
35897
35898
35899     /**
35900      * 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.
35901      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35902      * @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)
35903      * @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)
35904      * @return {Roo.UpdateManager} The UpdateManager
35905      */
35906     setUrl : function(url, params, loadOnce){
35907         if(this.refreshDelegate){
35908             this.removeListener("activate", this.refreshDelegate);
35909         }
35910         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35911         this.on("activate", this.refreshDelegate);
35912         return this.el.getUpdateManager();
35913     },
35914     
35915     _handleRefresh : function(url, params, loadOnce){
35916         if(!loadOnce || !this.loaded){
35917             var updater = this.el.getUpdateManager();
35918             updater.update(url, params, this._setLoaded.createDelegate(this));
35919         }
35920     },
35921     
35922     _setLoaded : function(){
35923         this.loaded = true;
35924     }, 
35925     
35926     /**
35927      * Returns this panel's id
35928      * @return {String} 
35929      */
35930     getId : function(){
35931         return this.el.id;
35932     },
35933     
35934     /** 
35935      * Returns this panel's element - used by regiosn to add.
35936      * @return {Roo.Element} 
35937      */
35938     getEl : function(){
35939         return this.wrapEl || this.el;
35940     },
35941     
35942     adjustForComponents : function(width, height)
35943     {
35944         //Roo.log('adjustForComponents ');
35945         if(this.resizeEl != this.el){
35946             width -= this.el.getFrameWidth('lr');
35947             height -= this.el.getFrameWidth('tb');
35948         }
35949         if(this.toolbar){
35950             var te = this.toolbar.getEl();
35951             height -= te.getHeight();
35952             te.setWidth(width);
35953         }
35954         if(this.footer){
35955             var te = this.footer.getEl();
35956             //Roo.log("footer:" + te.getHeight());
35957             
35958             height -= te.getHeight();
35959             te.setWidth(width);
35960         }
35961         
35962         
35963         if(this.adjustments){
35964             width += this.adjustments[0];
35965             height += this.adjustments[1];
35966         }
35967         return {"width": width, "height": height};
35968     },
35969     
35970     setSize : function(width, height){
35971         if(this.fitToFrame && !this.ignoreResize(width, height)){
35972             if(this.fitContainer && this.resizeEl != this.el){
35973                 this.el.setSize(width, height);
35974             }
35975             var size = this.adjustForComponents(width, height);
35976             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35977             this.fireEvent('resize', this, size.width, size.height);
35978         }
35979     },
35980     
35981     /**
35982      * Returns this panel's title
35983      * @return {String} 
35984      */
35985     getTitle : function(){
35986         return this.title;
35987     },
35988     
35989     /**
35990      * Set this panel's title
35991      * @param {String} title
35992      */
35993     setTitle : function(title){
35994         this.title = title;
35995         if(this.region){
35996             this.region.updatePanelTitle(this, title);
35997         }
35998     },
35999     
36000     /**
36001      * Returns true is this panel was configured to be closable
36002      * @return {Boolean} 
36003      */
36004     isClosable : function(){
36005         return this.closable;
36006     },
36007     
36008     beforeSlide : function(){
36009         this.el.clip();
36010         this.resizeEl.clip();
36011     },
36012     
36013     afterSlide : function(){
36014         this.el.unclip();
36015         this.resizeEl.unclip();
36016     },
36017     
36018     /**
36019      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36020      *   Will fail silently if the {@link #setUrl} method has not been called.
36021      *   This does not activate the panel, just updates its content.
36022      */
36023     refresh : function(){
36024         if(this.refreshDelegate){
36025            this.loaded = false;
36026            this.refreshDelegate();
36027         }
36028     },
36029     
36030     /**
36031      * Destroys this panel
36032      */
36033     destroy : function(){
36034         this.el.removeAllListeners();
36035         var tempEl = document.createElement("span");
36036         tempEl.appendChild(this.el.dom);
36037         tempEl.innerHTML = "";
36038         this.el.remove();
36039         this.el = null;
36040     },
36041     
36042     /**
36043      * form - if the content panel contains a form - this is a reference to it.
36044      * @type {Roo.form.Form}
36045      */
36046     form : false,
36047     /**
36048      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36049      *    This contains a reference to it.
36050      * @type {Roo.View}
36051      */
36052     view : false,
36053     
36054       /**
36055      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36056      * <pre><code>
36057
36058 layout.addxtype({
36059        xtype : 'Form',
36060        items: [ .... ]
36061    }
36062 );
36063
36064 </code></pre>
36065      * @param {Object} cfg Xtype definition of item to add.
36066      */
36067     
36068     addxtype : function(cfg) {
36069         // add form..
36070         if (cfg.xtype.match(/^Form$/)) {
36071             
36072             var el;
36073             //if (this.footer) {
36074             //    el = this.footer.container.insertSibling(false, 'before');
36075             //} else {
36076                 el = this.el.createChild();
36077             //}
36078
36079             this.form = new  Roo.form.Form(cfg);
36080             
36081             
36082             if ( this.form.allItems.length) {
36083                 this.form.render(el.dom);
36084             }
36085             return this.form;
36086         }
36087         // should only have one of theses..
36088         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36089             // views.. should not be just added - used named prop 'view''
36090             
36091             cfg.el = this.el.appendChild(document.createElement("div"));
36092             // factory?
36093             
36094             var ret = new Roo.factory(cfg);
36095              
36096              ret.render && ret.render(false, ''); // render blank..
36097             this.view = ret;
36098             return ret;
36099         }
36100         return false;
36101     }
36102 });
36103
36104 /**
36105  * @class Roo.GridPanel
36106  * @extends Roo.ContentPanel
36107  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36108  * @constructor
36109  * Create a new GridPanel.
36110  * @cfg {Roo.grid.Grid} grid The grid for this panel
36111  */
36112 Roo.GridPanel = function(grid, config){
36113     
36114     // universal ctor...
36115     if (typeof(grid.grid) != 'undefined') {
36116         config = grid;
36117         grid = config.grid;
36118     }
36119     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36120         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36121         
36122     this.wrapper.dom.appendChild(grid.getGridEl().dom);
36123     
36124     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36125     
36126     if(this.toolbar){
36127         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36128     }
36129     // xtype created footer. - not sure if will work as we normally have to render first..
36130     if (this.footer && !this.footer.el && this.footer.xtype) {
36131         
36132         this.footer.container = this.grid.getView().getFooterPanel(true);
36133         this.footer.dataSource = this.grid.dataSource;
36134         this.footer = Roo.factory(this.footer, Roo);
36135         
36136     }
36137     
36138     grid.monitorWindowResize = false; // turn off autosizing
36139     grid.autoHeight = false;
36140     grid.autoWidth = false;
36141     this.grid = grid;
36142     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36143 };
36144
36145 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36146     getId : function(){
36147         return this.grid.id;
36148     },
36149     
36150     /**
36151      * Returns the grid for this panel
36152      * @return {Roo.grid.Grid} 
36153      */
36154     getGrid : function(){
36155         return this.grid;    
36156     },
36157     
36158     setSize : function(width, height){
36159         if(!this.ignoreResize(width, height)){
36160             var grid = this.grid;
36161             var size = this.adjustForComponents(width, height);
36162             grid.getGridEl().setSize(size.width, size.height);
36163             grid.autoSize();
36164         }
36165     },
36166     
36167     beforeSlide : function(){
36168         this.grid.getView().scroller.clip();
36169     },
36170     
36171     afterSlide : function(){
36172         this.grid.getView().scroller.unclip();
36173     },
36174     
36175     destroy : function(){
36176         this.grid.destroy();
36177         delete this.grid;
36178         Roo.GridPanel.superclass.destroy.call(this); 
36179     }
36180 });
36181
36182
36183 /**
36184  * @class Roo.NestedLayoutPanel
36185  * @extends Roo.ContentPanel
36186  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36187  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
36188  *
36189  * 
36190  * @constructor
36191  * Create a new NestedLayoutPanel.
36192  * 
36193  * 
36194  * @param {Roo.BorderLayout} layout [required] The layout for this panel
36195  * @param {String/Object} config A string to set only the title or a config object
36196  */
36197 Roo.NestedLayoutPanel = function(layout, config)
36198 {
36199     // construct with only one argument..
36200     /* FIXME - implement nicer consturctors
36201     if (layout.layout) {
36202         config = layout;
36203         layout = config.layout;
36204         delete config.layout;
36205     }
36206     if (layout.xtype && !layout.getEl) {
36207         // then layout needs constructing..
36208         layout = Roo.factory(layout, Roo);
36209     }
36210     */
36211     
36212     
36213     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36214     
36215     layout.monitorWindowResize = false; // turn off autosizing
36216     this.layout = layout;
36217     this.layout.getEl().addClass("x-layout-nested-layout");
36218     
36219     
36220     
36221     
36222 };
36223
36224 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36225
36226     setSize : function(width, height){
36227         if(!this.ignoreResize(width, height)){
36228             var size = this.adjustForComponents(width, height);
36229             var el = this.layout.getEl();
36230             el.setSize(size.width, size.height);
36231             var touch = el.dom.offsetWidth;
36232             this.layout.layout();
36233             // ie requires a double layout on the first pass
36234             if(Roo.isIE && !this.initialized){
36235                 this.initialized = true;
36236                 this.layout.layout();
36237             }
36238         }
36239     },
36240     
36241     // activate all subpanels if not currently active..
36242     
36243     setActiveState : function(active){
36244         this.active = active;
36245         if(!active){
36246             this.fireEvent("deactivate", this);
36247             return;
36248         }
36249         
36250         this.fireEvent("activate", this);
36251         // not sure if this should happen before or after..
36252         if (!this.layout) {
36253             return; // should not happen..
36254         }
36255         var reg = false;
36256         for (var r in this.layout.regions) {
36257             reg = this.layout.getRegion(r);
36258             if (reg.getActivePanel()) {
36259                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36260                 reg.setActivePanel(reg.getActivePanel());
36261                 continue;
36262             }
36263             if (!reg.panels.length) {
36264                 continue;
36265             }
36266             reg.showPanel(reg.getPanel(0));
36267         }
36268         
36269         
36270         
36271         
36272     },
36273     
36274     /**
36275      * Returns the nested BorderLayout for this panel
36276      * @return {Roo.BorderLayout} 
36277      */
36278     getLayout : function(){
36279         return this.layout;
36280     },
36281     
36282      /**
36283      * Adds a xtype elements to the layout of the nested panel
36284      * <pre><code>
36285
36286 panel.addxtype({
36287        xtype : 'ContentPanel',
36288        region: 'west',
36289        items: [ .... ]
36290    }
36291 );
36292
36293 panel.addxtype({
36294         xtype : 'NestedLayoutPanel',
36295         region: 'west',
36296         layout: {
36297            center: { },
36298            west: { }   
36299         },
36300         items : [ ... list of content panels or nested layout panels.. ]
36301    }
36302 );
36303 </code></pre>
36304      * @param {Object} cfg Xtype definition of item to add.
36305      */
36306     addxtype : function(cfg) {
36307         return this.layout.addxtype(cfg);
36308     
36309     }
36310 });
36311
36312 Roo.ScrollPanel = function(el, config, content){
36313     config = config || {};
36314     config.fitToFrame = true;
36315     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36316     
36317     this.el.dom.style.overflow = "hidden";
36318     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36319     this.el.removeClass("x-layout-inactive-content");
36320     this.el.on("mousewheel", this.onWheel, this);
36321
36322     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
36323     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
36324     up.unselectable(); down.unselectable();
36325     up.on("click", this.scrollUp, this);
36326     down.on("click", this.scrollDown, this);
36327     up.addClassOnOver("x-scroller-btn-over");
36328     down.addClassOnOver("x-scroller-btn-over");
36329     up.addClassOnClick("x-scroller-btn-click");
36330     down.addClassOnClick("x-scroller-btn-click");
36331     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36332
36333     this.resizeEl = this.el;
36334     this.el = wrap; this.up = up; this.down = down;
36335 };
36336
36337 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36338     increment : 100,
36339     wheelIncrement : 5,
36340     scrollUp : function(){
36341         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36342     },
36343
36344     scrollDown : function(){
36345         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36346     },
36347
36348     afterScroll : function(){
36349         var el = this.resizeEl;
36350         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36351         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36352         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36353     },
36354
36355     setSize : function(){
36356         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36357         this.afterScroll();
36358     },
36359
36360     onWheel : function(e){
36361         var d = e.getWheelDelta();
36362         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36363         this.afterScroll();
36364         e.stopEvent();
36365     },
36366
36367     setContent : function(content, loadScripts){
36368         this.resizeEl.update(content, loadScripts);
36369     }
36370
36371 });
36372
36373
36374
36375 /**
36376  * @class Roo.TreePanel
36377  * @extends Roo.ContentPanel
36378  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36379  * Treepanel component
36380  * 
36381  * @constructor
36382  * Create a new TreePanel. - defaults to fit/scoll contents.
36383  * @param {String/Object} config A string to set only the panel's title, or a config object
36384  */
36385 Roo.TreePanel = function(config){
36386     var el = config.el;
36387     var tree = config.tree;
36388     delete config.tree; 
36389     delete config.el; // hopefull!
36390     
36391     // wrapper for IE7 strict & safari scroll issue
36392     
36393     var treeEl = el.createChild();
36394     config.resizeEl = treeEl;
36395     
36396     
36397     
36398     Roo.TreePanel.superclass.constructor.call(this, el, config);
36399  
36400  
36401     this.tree = new Roo.tree.TreePanel(treeEl , tree);
36402     //console.log(tree);
36403     this.on('activate', function()
36404     {
36405         if (this.tree.rendered) {
36406             return;
36407         }
36408         //console.log('render tree');
36409         this.tree.render();
36410     });
36411     // this should not be needed.. - it's actually the 'el' that resizes?
36412     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36413     
36414     //this.on('resize',  function (cp, w, h) {
36415     //        this.tree.innerCt.setWidth(w);
36416     //        this.tree.innerCt.setHeight(h);
36417     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
36418     //});
36419
36420         
36421     
36422 };
36423
36424 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
36425     fitToFrame : true,
36426     autoScroll : true,
36427     /*
36428      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36429      */
36430     tree : false
36431
36432 });
36433
36434
36435
36436
36437
36438
36439
36440
36441
36442
36443
36444 /*
36445  * Based on:
36446  * Ext JS Library 1.1.1
36447  * Copyright(c) 2006-2007, Ext JS, LLC.
36448  *
36449  * Originally Released Under LGPL - original licence link has changed is not relivant.
36450  *
36451  * Fork - LGPL
36452  * <script type="text/javascript">
36453  */
36454  
36455
36456 /**
36457  * @class Roo.ReaderLayout
36458  * @extends Roo.BorderLayout
36459  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
36460  * center region containing two nested regions (a top one for a list view and one for item preview below),
36461  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36462  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36463  * expedites the setup of the overall layout and regions for this common application style.
36464  * Example:
36465  <pre><code>
36466 var reader = new Roo.ReaderLayout();
36467 var CP = Roo.ContentPanel;  // shortcut for adding
36468
36469 reader.beginUpdate();
36470 reader.add("north", new CP("north", "North"));
36471 reader.add("west", new CP("west", {title: "West"}));
36472 reader.add("east", new CP("east", {title: "East"}));
36473
36474 reader.regions.listView.add(new CP("listView", "List"));
36475 reader.regions.preview.add(new CP("preview", "Preview"));
36476 reader.endUpdate();
36477 </code></pre>
36478 * @constructor
36479 * Create a new ReaderLayout
36480 * @param {Object} config Configuration options
36481 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36482 * document.body if omitted)
36483 */
36484 Roo.ReaderLayout = function(config, renderTo){
36485     var c = config || {size:{}};
36486     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36487         north: c.north !== false ? Roo.apply({
36488             split:false,
36489             initialSize: 32,
36490             titlebar: false
36491         }, c.north) : false,
36492         west: c.west !== false ? Roo.apply({
36493             split:true,
36494             initialSize: 200,
36495             minSize: 175,
36496             maxSize: 400,
36497             titlebar: true,
36498             collapsible: true,
36499             animate: true,
36500             margins:{left:5,right:0,bottom:5,top:5},
36501             cmargins:{left:5,right:5,bottom:5,top:5}
36502         }, c.west) : false,
36503         east: c.east !== false ? Roo.apply({
36504             split:true,
36505             initialSize: 200,
36506             minSize: 175,
36507             maxSize: 400,
36508             titlebar: true,
36509             collapsible: true,
36510             animate: true,
36511             margins:{left:0,right:5,bottom:5,top:5},
36512             cmargins:{left:5,right:5,bottom:5,top:5}
36513         }, c.east) : false,
36514         center: Roo.apply({
36515             tabPosition: 'top',
36516             autoScroll:false,
36517             closeOnTab: true,
36518             titlebar:false,
36519             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36520         }, c.center)
36521     });
36522
36523     this.el.addClass('x-reader');
36524
36525     this.beginUpdate();
36526
36527     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36528         south: c.preview !== false ? Roo.apply({
36529             split:true,
36530             initialSize: 200,
36531             minSize: 100,
36532             autoScroll:true,
36533             collapsible:true,
36534             titlebar: true,
36535             cmargins:{top:5,left:0, right:0, bottom:0}
36536         }, c.preview) : false,
36537         center: Roo.apply({
36538             autoScroll:false,
36539             titlebar:false,
36540             minHeight:200
36541         }, c.listView)
36542     });
36543     this.add('center', new Roo.NestedLayoutPanel(inner,
36544             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36545
36546     this.endUpdate();
36547
36548     this.regions.preview = inner.getRegion('south');
36549     this.regions.listView = inner.getRegion('center');
36550 };
36551
36552 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36553  * Based on:
36554  * Ext JS Library 1.1.1
36555  * Copyright(c) 2006-2007, Ext JS, LLC.
36556  *
36557  * Originally Released Under LGPL - original licence link has changed is not relivant.
36558  *
36559  * Fork - LGPL
36560  * <script type="text/javascript">
36561  */
36562  
36563 /**
36564  * @class Roo.grid.Grid
36565  * @extends Roo.util.Observable
36566  * This class represents the primary interface of a component based grid control.
36567  * <br><br>Usage:<pre><code>
36568  var grid = new Roo.grid.Grid("my-container-id", {
36569      ds: myDataStore,
36570      cm: myColModel,
36571      selModel: mySelectionModel,
36572      autoSizeColumns: true,
36573      monitorWindowResize: false,
36574      trackMouseOver: true
36575  });
36576  // set any options
36577  grid.render();
36578  * </code></pre>
36579  * <b>Common Problems:</b><br/>
36580  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36581  * element will correct this<br/>
36582  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36583  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36584  * are unpredictable.<br/>
36585  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36586  * grid to calculate dimensions/offsets.<br/>
36587   * @constructor
36588  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36589  * The container MUST have some type of size defined for the grid to fill. The container will be
36590  * automatically set to position relative if it isn't already.
36591  * @param {Object} config A config object that sets properties on this grid.
36592  */
36593 Roo.grid.Grid = function(container, config){
36594         // initialize the container
36595         this.container = Roo.get(container);
36596         this.container.update("");
36597         this.container.setStyle("overflow", "hidden");
36598     this.container.addClass('x-grid-container');
36599
36600     this.id = this.container.id;
36601
36602     Roo.apply(this, config);
36603     // check and correct shorthanded configs
36604     if(this.ds){
36605         this.dataSource = this.ds;
36606         delete this.ds;
36607     }
36608     if(this.cm){
36609         this.colModel = this.cm;
36610         delete this.cm;
36611     }
36612     if(this.sm){
36613         this.selModel = this.sm;
36614         delete this.sm;
36615     }
36616
36617     if (this.selModel) {
36618         this.selModel = Roo.factory(this.selModel, Roo.grid);
36619         this.sm = this.selModel;
36620         this.sm.xmodule = this.xmodule || false;
36621     }
36622     if (typeof(this.colModel.config) == 'undefined') {
36623         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36624         this.cm = this.colModel;
36625         this.cm.xmodule = this.xmodule || false;
36626     }
36627     if (this.dataSource) {
36628         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36629         this.ds = this.dataSource;
36630         this.ds.xmodule = this.xmodule || false;
36631          
36632     }
36633     
36634     
36635     
36636     if(this.width){
36637         this.container.setWidth(this.width);
36638     }
36639
36640     if(this.height){
36641         this.container.setHeight(this.height);
36642     }
36643     /** @private */
36644         this.addEvents({
36645         // raw events
36646         /**
36647          * @event click
36648          * The raw click event for the entire grid.
36649          * @param {Roo.EventObject} e
36650          */
36651         "click" : true,
36652         /**
36653          * @event dblclick
36654          * The raw dblclick event for the entire grid.
36655          * @param {Roo.EventObject} e
36656          */
36657         "dblclick" : true,
36658         /**
36659          * @event contextmenu
36660          * The raw contextmenu event for the entire grid.
36661          * @param {Roo.EventObject} e
36662          */
36663         "contextmenu" : true,
36664         /**
36665          * @event mousedown
36666          * The raw mousedown event for the entire grid.
36667          * @param {Roo.EventObject} e
36668          */
36669         "mousedown" : true,
36670         /**
36671          * @event mouseup
36672          * The raw mouseup event for the entire grid.
36673          * @param {Roo.EventObject} e
36674          */
36675         "mouseup" : true,
36676         /**
36677          * @event mouseover
36678          * The raw mouseover event for the entire grid.
36679          * @param {Roo.EventObject} e
36680          */
36681         "mouseover" : true,
36682         /**
36683          * @event mouseout
36684          * The raw mouseout event for the entire grid.
36685          * @param {Roo.EventObject} e
36686          */
36687         "mouseout" : true,
36688         /**
36689          * @event keypress
36690          * The raw keypress event for the entire grid.
36691          * @param {Roo.EventObject} e
36692          */
36693         "keypress" : true,
36694         /**
36695          * @event keydown
36696          * The raw keydown event for the entire grid.
36697          * @param {Roo.EventObject} e
36698          */
36699         "keydown" : true,
36700
36701         // custom events
36702
36703         /**
36704          * @event cellclick
36705          * Fires when a cell is clicked
36706          * @param {Grid} this
36707          * @param {Number} rowIndex
36708          * @param {Number} columnIndex
36709          * @param {Roo.EventObject} e
36710          */
36711         "cellclick" : true,
36712         /**
36713          * @event celldblclick
36714          * Fires when a cell is double clicked
36715          * @param {Grid} this
36716          * @param {Number} rowIndex
36717          * @param {Number} columnIndex
36718          * @param {Roo.EventObject} e
36719          */
36720         "celldblclick" : true,
36721         /**
36722          * @event rowclick
36723          * Fires when a row is clicked
36724          * @param {Grid} this
36725          * @param {Number} rowIndex
36726          * @param {Roo.EventObject} e
36727          */
36728         "rowclick" : true,
36729         /**
36730          * @event rowdblclick
36731          * Fires when a row is double clicked
36732          * @param {Grid} this
36733          * @param {Number} rowIndex
36734          * @param {Roo.EventObject} e
36735          */
36736         "rowdblclick" : true,
36737         /**
36738          * @event headerclick
36739          * Fires when a header is clicked
36740          * @param {Grid} this
36741          * @param {Number} columnIndex
36742          * @param {Roo.EventObject} e
36743          */
36744         "headerclick" : true,
36745         /**
36746          * @event headerdblclick
36747          * Fires when a header cell is double clicked
36748          * @param {Grid} this
36749          * @param {Number} columnIndex
36750          * @param {Roo.EventObject} e
36751          */
36752         "headerdblclick" : true,
36753         /**
36754          * @event rowcontextmenu
36755          * Fires when a row is right clicked
36756          * @param {Grid} this
36757          * @param {Number} rowIndex
36758          * @param {Roo.EventObject} e
36759          */
36760         "rowcontextmenu" : true,
36761         /**
36762          * @event cellcontextmenu
36763          * Fires when a cell is right clicked
36764          * @param {Grid} this
36765          * @param {Number} rowIndex
36766          * @param {Number} cellIndex
36767          * @param {Roo.EventObject} e
36768          */
36769          "cellcontextmenu" : true,
36770         /**
36771          * @event headercontextmenu
36772          * Fires when a header is right clicked
36773          * @param {Grid} this
36774          * @param {Number} columnIndex
36775          * @param {Roo.EventObject} e
36776          */
36777         "headercontextmenu" : true,
36778         /**
36779          * @event bodyscroll
36780          * Fires when the body element is scrolled
36781          * @param {Number} scrollLeft
36782          * @param {Number} scrollTop
36783          */
36784         "bodyscroll" : true,
36785         /**
36786          * @event columnresize
36787          * Fires when the user resizes a column
36788          * @param {Number} columnIndex
36789          * @param {Number} newSize
36790          */
36791         "columnresize" : true,
36792         /**
36793          * @event columnmove
36794          * Fires when the user moves a column
36795          * @param {Number} oldIndex
36796          * @param {Number} newIndex
36797          */
36798         "columnmove" : true,
36799         /**
36800          * @event startdrag
36801          * Fires when row(s) start being dragged
36802          * @param {Grid} this
36803          * @param {Roo.GridDD} dd The drag drop object
36804          * @param {event} e The raw browser event
36805          */
36806         "startdrag" : true,
36807         /**
36808          * @event enddrag
36809          * Fires when a drag operation is complete
36810          * @param {Grid} this
36811          * @param {Roo.GridDD} dd The drag drop object
36812          * @param {event} e The raw browser event
36813          */
36814         "enddrag" : true,
36815         /**
36816          * @event dragdrop
36817          * Fires when dragged row(s) are dropped on a valid DD target
36818          * @param {Grid} this
36819          * @param {Roo.GridDD} dd The drag drop object
36820          * @param {String} targetId The target drag drop object
36821          * @param {event} e The raw browser event
36822          */
36823         "dragdrop" : true,
36824         /**
36825          * @event dragover
36826          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36827          * @param {Grid} this
36828          * @param {Roo.GridDD} dd The drag drop object
36829          * @param {String} targetId The target drag drop object
36830          * @param {event} e The raw browser event
36831          */
36832         "dragover" : true,
36833         /**
36834          * @event dragenter
36835          *  Fires when the dragged row(s) first cross another DD target while being dragged
36836          * @param {Grid} this
36837          * @param {Roo.GridDD} dd The drag drop object
36838          * @param {String} targetId The target drag drop object
36839          * @param {event} e The raw browser event
36840          */
36841         "dragenter" : true,
36842         /**
36843          * @event dragout
36844          * Fires when the dragged row(s) leave another DD target while being dragged
36845          * @param {Grid} this
36846          * @param {Roo.GridDD} dd The drag drop object
36847          * @param {String} targetId The target drag drop object
36848          * @param {event} e The raw browser event
36849          */
36850         "dragout" : true,
36851         /**
36852          * @event rowclass
36853          * Fires when a row is rendered, so you can change add a style to it.
36854          * @param {GridView} gridview   The grid view
36855          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36856          */
36857         'rowclass' : true,
36858
36859         /**
36860          * @event render
36861          * Fires when the grid is rendered
36862          * @param {Grid} grid
36863          */
36864         'render' : true
36865     });
36866
36867     Roo.grid.Grid.superclass.constructor.call(this);
36868 };
36869 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36870     
36871     /**
36872          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
36873          */
36874         /**
36875          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
36876          */
36877         /**
36878          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
36879          */
36880         /**
36881          * @cfg {Roo.grid.Store} ds The data store for the grid
36882          */
36883         /**
36884          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
36885          */
36886         /**
36887      * @cfg {String} ddGroup - drag drop group.
36888      */
36889       /**
36890      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
36891      */
36892
36893     /**
36894      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36895      */
36896     minColumnWidth : 25,
36897
36898     /**
36899      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36900      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36901      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36902      */
36903     autoSizeColumns : false,
36904
36905     /**
36906      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36907      */
36908     autoSizeHeaders : true,
36909
36910     /**
36911      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36912      */
36913     monitorWindowResize : true,
36914
36915     /**
36916      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36917      * rows measured to get a columns size. Default is 0 (all rows).
36918      */
36919     maxRowsToMeasure : 0,
36920
36921     /**
36922      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36923      */
36924     trackMouseOver : true,
36925
36926     /**
36927     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36928     */
36929       /**
36930     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
36931     */
36932     
36933     /**
36934     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36935     */
36936     enableDragDrop : false,
36937     
36938     /**
36939     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36940     */
36941     enableColumnMove : true,
36942     
36943     /**
36944     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36945     */
36946     enableColumnHide : true,
36947     
36948     /**
36949     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36950     */
36951     enableRowHeightSync : false,
36952     
36953     /**
36954     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36955     */
36956     stripeRows : true,
36957     
36958     /**
36959     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36960     */
36961     autoHeight : false,
36962
36963     /**
36964      * @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.
36965      */
36966     autoExpandColumn : false,
36967
36968     /**
36969     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36970     * Default is 50.
36971     */
36972     autoExpandMin : 50,
36973
36974     /**
36975     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36976     */
36977     autoExpandMax : 1000,
36978
36979     /**
36980     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36981     */
36982     view : null,
36983
36984     /**
36985     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36986     */
36987     loadMask : false,
36988     /**
36989     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36990     */
36991     dropTarget: false,
36992      /**
36993     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
36994     */ 
36995     sortColMenu : false,
36996     
36997     // private
36998     rendered : false,
36999
37000     /**
37001     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37002     * of a fixed width. Default is false.
37003     */
37004     /**
37005     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37006     */
37007     
37008     
37009     /**
37010     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37011     * %0 is replaced with the number of selected rows.
37012     */
37013     ddText : "{0} selected row{1}",
37014     
37015     
37016     /**
37017      * Called once after all setup has been completed and the grid is ready to be rendered.
37018      * @return {Roo.grid.Grid} this
37019      */
37020     render : function()
37021     {
37022         var c = this.container;
37023         // try to detect autoHeight/width mode
37024         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37025             this.autoHeight = true;
37026         }
37027         var view = this.getView();
37028         view.init(this);
37029
37030         c.on("click", this.onClick, this);
37031         c.on("dblclick", this.onDblClick, this);
37032         c.on("contextmenu", this.onContextMenu, this);
37033         c.on("keydown", this.onKeyDown, this);
37034         if (Roo.isTouch) {
37035             c.on("touchstart", this.onTouchStart, this);
37036         }
37037
37038         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37039
37040         this.getSelectionModel().init(this);
37041
37042         view.render();
37043
37044         if(this.loadMask){
37045             this.loadMask = new Roo.LoadMask(this.container,
37046                     Roo.apply({store:this.dataSource}, this.loadMask));
37047         }
37048         
37049         
37050         if (this.toolbar && this.toolbar.xtype) {
37051             this.toolbar.container = this.getView().getHeaderPanel(true);
37052             this.toolbar = new Roo.Toolbar(this.toolbar);
37053         }
37054         if (this.footer && this.footer.xtype) {
37055             this.footer.dataSource = this.getDataSource();
37056             this.footer.container = this.getView().getFooterPanel(true);
37057             this.footer = Roo.factory(this.footer, Roo);
37058         }
37059         if (this.dropTarget && this.dropTarget.xtype) {
37060             delete this.dropTarget.xtype;
37061             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37062         }
37063         
37064         
37065         this.rendered = true;
37066         this.fireEvent('render', this);
37067         return this;
37068     },
37069
37070     /**
37071      * Reconfigures the grid to use a different Store and Column Model.
37072      * The View will be bound to the new objects and refreshed.
37073      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37074      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37075      */
37076     reconfigure : function(dataSource, colModel){
37077         if(this.loadMask){
37078             this.loadMask.destroy();
37079             this.loadMask = new Roo.LoadMask(this.container,
37080                     Roo.apply({store:dataSource}, this.loadMask));
37081         }
37082         this.view.bind(dataSource, colModel);
37083         this.dataSource = dataSource;
37084         this.colModel = colModel;
37085         this.view.refresh(true);
37086     },
37087     /**
37088      * addColumns
37089      * Add's a column, default at the end..
37090      
37091      * @param {int} position to add (default end)
37092      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
37093      */
37094     addColumns : function(pos, ar)
37095     {
37096         
37097         for (var i =0;i< ar.length;i++) {
37098             var cfg = ar[i];
37099             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37100             this.cm.lookup[cfg.id] = cfg;
37101         }
37102         
37103         
37104         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37105             pos = this.cm.config.length; //this.cm.config.push(cfg);
37106         } 
37107         pos = Math.max(0,pos);
37108         ar.unshift(0);
37109         ar.unshift(pos);
37110         this.cm.config.splice.apply(this.cm.config, ar);
37111         
37112         
37113         
37114         this.view.generateRules(this.cm);
37115         this.view.refresh(true);
37116         
37117     },
37118     
37119     
37120     
37121     
37122     // private
37123     onKeyDown : function(e){
37124         this.fireEvent("keydown", e);
37125     },
37126
37127     /**
37128      * Destroy this grid.
37129      * @param {Boolean} removeEl True to remove the element
37130      */
37131     destroy : function(removeEl, keepListeners){
37132         if(this.loadMask){
37133             this.loadMask.destroy();
37134         }
37135         var c = this.container;
37136         c.removeAllListeners();
37137         this.view.destroy();
37138         this.colModel.purgeListeners();
37139         if(!keepListeners){
37140             this.purgeListeners();
37141         }
37142         c.update("");
37143         if(removeEl === true){
37144             c.remove();
37145         }
37146     },
37147
37148     // private
37149     processEvent : function(name, e){
37150         // does this fire select???
37151         //Roo.log('grid:processEvent '  + name);
37152         
37153         if (name != 'touchstart' ) {
37154             this.fireEvent(name, e);    
37155         }
37156         
37157         var t = e.getTarget();
37158         var v = this.view;
37159         var header = v.findHeaderIndex(t);
37160         if(header !== false){
37161             var ename = name == 'touchstart' ? 'click' : name;
37162              
37163             this.fireEvent("header" + ename, this, header, e);
37164         }else{
37165             var row = v.findRowIndex(t);
37166             var cell = v.findCellIndex(t);
37167             if (name == 'touchstart') {
37168                 // first touch is always a click.
37169                 // hopefull this happens after selection is updated.?
37170                 name = false;
37171                 
37172                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37173                     var cs = this.selModel.getSelectedCell();
37174                     if (row == cs[0] && cell == cs[1]){
37175                         name = 'dblclick';
37176                     }
37177                 }
37178                 if (typeof(this.selModel.getSelections) != 'undefined') {
37179                     var cs = this.selModel.getSelections();
37180                     var ds = this.dataSource;
37181                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
37182                         name = 'dblclick';
37183                     }
37184                 }
37185                 if (!name) {
37186                     return;
37187                 }
37188             }
37189             
37190             
37191             if(row !== false){
37192                 this.fireEvent("row" + name, this, row, e);
37193                 if(cell !== false){
37194                     this.fireEvent("cell" + name, this, row, cell, e);
37195                 }
37196             }
37197         }
37198     },
37199
37200     // private
37201     onClick : function(e){
37202         this.processEvent("click", e);
37203     },
37204    // private
37205     onTouchStart : function(e){
37206         this.processEvent("touchstart", e);
37207     },
37208
37209     // private
37210     onContextMenu : function(e, t){
37211         this.processEvent("contextmenu", e);
37212     },
37213
37214     // private
37215     onDblClick : function(e){
37216         this.processEvent("dblclick", e);
37217     },
37218
37219     // private
37220     walkCells : function(row, col, step, fn, scope){
37221         var cm = this.colModel, clen = cm.getColumnCount();
37222         var ds = this.dataSource, rlen = ds.getCount(), first = true;
37223         if(step < 0){
37224             if(col < 0){
37225                 row--;
37226                 first = false;
37227             }
37228             while(row >= 0){
37229                 if(!first){
37230                     col = clen-1;
37231                 }
37232                 first = false;
37233                 while(col >= 0){
37234                     if(fn.call(scope || this, row, col, cm) === true){
37235                         return [row, col];
37236                     }
37237                     col--;
37238                 }
37239                 row--;
37240             }
37241         } else {
37242             if(col >= clen){
37243                 row++;
37244                 first = false;
37245             }
37246             while(row < rlen){
37247                 if(!first){
37248                     col = 0;
37249                 }
37250                 first = false;
37251                 while(col < clen){
37252                     if(fn.call(scope || this, row, col, cm) === true){
37253                         return [row, col];
37254                     }
37255                     col++;
37256                 }
37257                 row++;
37258             }
37259         }
37260         return null;
37261     },
37262
37263     // private
37264     getSelections : function(){
37265         return this.selModel.getSelections();
37266     },
37267
37268     /**
37269      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37270      * but if manual update is required this method will initiate it.
37271      */
37272     autoSize : function(){
37273         if(this.rendered){
37274             this.view.layout();
37275             if(this.view.adjustForScroll){
37276                 this.view.adjustForScroll();
37277             }
37278         }
37279     },
37280
37281     /**
37282      * Returns the grid's underlying element.
37283      * @return {Element} The element
37284      */
37285     getGridEl : function(){
37286         return this.container;
37287     },
37288
37289     // private for compatibility, overridden by editor grid
37290     stopEditing : function(){},
37291
37292     /**
37293      * Returns the grid's SelectionModel.
37294      * @return {SelectionModel}
37295      */
37296     getSelectionModel : function(){
37297         if(!this.selModel){
37298             this.selModel = new Roo.grid.RowSelectionModel();
37299         }
37300         return this.selModel;
37301     },
37302
37303     /**
37304      * Returns the grid's DataSource.
37305      * @return {DataSource}
37306      */
37307     getDataSource : function(){
37308         return this.dataSource;
37309     },
37310
37311     /**
37312      * Returns the grid's ColumnModel.
37313      * @return {ColumnModel}
37314      */
37315     getColumnModel : function(){
37316         return this.colModel;
37317     },
37318
37319     /**
37320      * Returns the grid's GridView object.
37321      * @return {GridView}
37322      */
37323     getView : function(){
37324         if(!this.view){
37325             this.view = new Roo.grid.GridView(this.viewConfig);
37326             this.relayEvents(this.view, [
37327                 "beforerowremoved", "beforerowsinserted",
37328                 "beforerefresh", "rowremoved",
37329                 "rowsinserted", "rowupdated" ,"refresh"
37330             ]);
37331         }
37332         return this.view;
37333     },
37334     /**
37335      * Called to get grid's drag proxy text, by default returns this.ddText.
37336      * Override this to put something different in the dragged text.
37337      * @return {String}
37338      */
37339     getDragDropText : function(){
37340         var count = this.selModel.getCount();
37341         return String.format(this.ddText, count, count == 1 ? '' : 's');
37342     }
37343 });
37344 /*
37345  * Based on:
37346  * Ext JS Library 1.1.1
37347  * Copyright(c) 2006-2007, Ext JS, LLC.
37348  *
37349  * Originally Released Under LGPL - original licence link has changed is not relivant.
37350  *
37351  * Fork - LGPL
37352  * <script type="text/javascript">
37353  */
37354  /**
37355  * @class Roo.grid.AbstractGridView
37356  * @extends Roo.util.Observable
37357  * @abstract
37358  * Abstract base class for grid Views
37359  * @constructor
37360  */
37361 Roo.grid.AbstractGridView = function(){
37362         this.grid = null;
37363         
37364         this.events = {
37365             "beforerowremoved" : true,
37366             "beforerowsinserted" : true,
37367             "beforerefresh" : true,
37368             "rowremoved" : true,
37369             "rowsinserted" : true,
37370             "rowupdated" : true,
37371             "refresh" : true
37372         };
37373     Roo.grid.AbstractGridView.superclass.constructor.call(this);
37374 };
37375
37376 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37377     rowClass : "x-grid-row",
37378     cellClass : "x-grid-cell",
37379     tdClass : "x-grid-td",
37380     hdClass : "x-grid-hd",
37381     splitClass : "x-grid-hd-split",
37382     
37383     init: function(grid){
37384         this.grid = grid;
37385                 var cid = this.grid.getGridEl().id;
37386         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37387         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37388         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37389         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37390         },
37391         
37392     getColumnRenderers : function(){
37393         var renderers = [];
37394         var cm = this.grid.colModel;
37395         var colCount = cm.getColumnCount();
37396         for(var i = 0; i < colCount; i++){
37397             renderers[i] = cm.getRenderer(i);
37398         }
37399         return renderers;
37400     },
37401     
37402     getColumnIds : function(){
37403         var ids = [];
37404         var cm = this.grid.colModel;
37405         var colCount = cm.getColumnCount();
37406         for(var i = 0; i < colCount; i++){
37407             ids[i] = cm.getColumnId(i);
37408         }
37409         return ids;
37410     },
37411     
37412     getDataIndexes : function(){
37413         if(!this.indexMap){
37414             this.indexMap = this.buildIndexMap();
37415         }
37416         return this.indexMap.colToData;
37417     },
37418     
37419     getColumnIndexByDataIndex : function(dataIndex){
37420         if(!this.indexMap){
37421             this.indexMap = this.buildIndexMap();
37422         }
37423         return this.indexMap.dataToCol[dataIndex];
37424     },
37425     
37426     /**
37427      * Set a css style for a column dynamically. 
37428      * @param {Number} colIndex The index of the column
37429      * @param {String} name The css property name
37430      * @param {String} value The css value
37431      */
37432     setCSSStyle : function(colIndex, name, value){
37433         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37434         Roo.util.CSS.updateRule(selector, name, value);
37435     },
37436     
37437     generateRules : function(cm){
37438         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37439         Roo.util.CSS.removeStyleSheet(rulesId);
37440         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37441             var cid = cm.getColumnId(i);
37442             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37443                          this.tdSelector, cid, " {\n}\n",
37444                          this.hdSelector, cid, " {\n}\n",
37445                          this.splitSelector, cid, " {\n}\n");
37446         }
37447         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37448     }
37449 });/*
37450  * Based on:
37451  * Ext JS Library 1.1.1
37452  * Copyright(c) 2006-2007, Ext JS, LLC.
37453  *
37454  * Originally Released Under LGPL - original licence link has changed is not relivant.
37455  *
37456  * Fork - LGPL
37457  * <script type="text/javascript">
37458  */
37459
37460 // private
37461 // This is a support class used internally by the Grid components
37462 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37463     this.grid = grid;
37464     this.view = grid.getView();
37465     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37466     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37467     if(hd2){
37468         this.setHandleElId(Roo.id(hd));
37469         this.setOuterHandleElId(Roo.id(hd2));
37470     }
37471     this.scroll = false;
37472 };
37473 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37474     maxDragWidth: 120,
37475     getDragData : function(e){
37476         var t = Roo.lib.Event.getTarget(e);
37477         var h = this.view.findHeaderCell(t);
37478         if(h){
37479             return {ddel: h.firstChild, header:h};
37480         }
37481         return false;
37482     },
37483
37484     onInitDrag : function(e){
37485         this.view.headersDisabled = true;
37486         var clone = this.dragData.ddel.cloneNode(true);
37487         clone.id = Roo.id();
37488         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37489         this.proxy.update(clone);
37490         return true;
37491     },
37492
37493     afterValidDrop : function(){
37494         var v = this.view;
37495         setTimeout(function(){
37496             v.headersDisabled = false;
37497         }, 50);
37498     },
37499
37500     afterInvalidDrop : function(){
37501         var v = this.view;
37502         setTimeout(function(){
37503             v.headersDisabled = false;
37504         }, 50);
37505     }
37506 });
37507 /*
37508  * Based on:
37509  * Ext JS Library 1.1.1
37510  * Copyright(c) 2006-2007, Ext JS, LLC.
37511  *
37512  * Originally Released Under LGPL - original licence link has changed is not relivant.
37513  *
37514  * Fork - LGPL
37515  * <script type="text/javascript">
37516  */
37517 // private
37518 // This is a support class used internally by the Grid components
37519 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37520     this.grid = grid;
37521     this.view = grid.getView();
37522     // split the proxies so they don't interfere with mouse events
37523     this.proxyTop = Roo.DomHelper.append(document.body, {
37524         cls:"col-move-top", html:"&#160;"
37525     }, true);
37526     this.proxyBottom = Roo.DomHelper.append(document.body, {
37527         cls:"col-move-bottom", html:"&#160;"
37528     }, true);
37529     this.proxyTop.hide = this.proxyBottom.hide = function(){
37530         this.setLeftTop(-100,-100);
37531         this.setStyle("visibility", "hidden");
37532     };
37533     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37534     // temporarily disabled
37535     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37536     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37537 };
37538 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37539     proxyOffsets : [-4, -9],
37540     fly: Roo.Element.fly,
37541
37542     getTargetFromEvent : function(e){
37543         var t = Roo.lib.Event.getTarget(e);
37544         var cindex = this.view.findCellIndex(t);
37545         if(cindex !== false){
37546             return this.view.getHeaderCell(cindex);
37547         }
37548         return null;
37549     },
37550
37551     nextVisible : function(h){
37552         var v = this.view, cm = this.grid.colModel;
37553         h = h.nextSibling;
37554         while(h){
37555             if(!cm.isHidden(v.getCellIndex(h))){
37556                 return h;
37557             }
37558             h = h.nextSibling;
37559         }
37560         return null;
37561     },
37562
37563     prevVisible : function(h){
37564         var v = this.view, cm = this.grid.colModel;
37565         h = h.prevSibling;
37566         while(h){
37567             if(!cm.isHidden(v.getCellIndex(h))){
37568                 return h;
37569             }
37570             h = h.prevSibling;
37571         }
37572         return null;
37573     },
37574
37575     positionIndicator : function(h, n, e){
37576         var x = Roo.lib.Event.getPageX(e);
37577         var r = Roo.lib.Dom.getRegion(n.firstChild);
37578         var px, pt, py = r.top + this.proxyOffsets[1];
37579         if((r.right - x) <= (r.right-r.left)/2){
37580             px = r.right+this.view.borderWidth;
37581             pt = "after";
37582         }else{
37583             px = r.left;
37584             pt = "before";
37585         }
37586         var oldIndex = this.view.getCellIndex(h);
37587         var newIndex = this.view.getCellIndex(n);
37588
37589         if(this.grid.colModel.isFixed(newIndex)){
37590             return false;
37591         }
37592
37593         var locked = this.grid.colModel.isLocked(newIndex);
37594
37595         if(pt == "after"){
37596             newIndex++;
37597         }
37598         if(oldIndex < newIndex){
37599             newIndex--;
37600         }
37601         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37602             return false;
37603         }
37604         px +=  this.proxyOffsets[0];
37605         this.proxyTop.setLeftTop(px, py);
37606         this.proxyTop.show();
37607         if(!this.bottomOffset){
37608             this.bottomOffset = this.view.mainHd.getHeight();
37609         }
37610         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37611         this.proxyBottom.show();
37612         return pt;
37613     },
37614
37615     onNodeEnter : function(n, dd, e, data){
37616         if(data.header != n){
37617             this.positionIndicator(data.header, n, e);
37618         }
37619     },
37620
37621     onNodeOver : function(n, dd, e, data){
37622         var result = false;
37623         if(data.header != n){
37624             result = this.positionIndicator(data.header, n, e);
37625         }
37626         if(!result){
37627             this.proxyTop.hide();
37628             this.proxyBottom.hide();
37629         }
37630         return result ? this.dropAllowed : this.dropNotAllowed;
37631     },
37632
37633     onNodeOut : function(n, dd, e, data){
37634         this.proxyTop.hide();
37635         this.proxyBottom.hide();
37636     },
37637
37638     onNodeDrop : function(n, dd, e, data){
37639         var h = data.header;
37640         if(h != n){
37641             var cm = this.grid.colModel;
37642             var x = Roo.lib.Event.getPageX(e);
37643             var r = Roo.lib.Dom.getRegion(n.firstChild);
37644             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37645             var oldIndex = this.view.getCellIndex(h);
37646             var newIndex = this.view.getCellIndex(n);
37647             var locked = cm.isLocked(newIndex);
37648             if(pt == "after"){
37649                 newIndex++;
37650             }
37651             if(oldIndex < newIndex){
37652                 newIndex--;
37653             }
37654             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37655                 return false;
37656             }
37657             cm.setLocked(oldIndex, locked, true);
37658             cm.moveColumn(oldIndex, newIndex);
37659             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37660             return true;
37661         }
37662         return false;
37663     }
37664 });
37665 /*
37666  * Based on:
37667  * Ext JS Library 1.1.1
37668  * Copyright(c) 2006-2007, Ext JS, LLC.
37669  *
37670  * Originally Released Under LGPL - original licence link has changed is not relivant.
37671  *
37672  * Fork - LGPL
37673  * <script type="text/javascript">
37674  */
37675   
37676 /**
37677  * @class Roo.grid.GridView
37678  * @extends Roo.util.Observable
37679  *
37680  * @constructor
37681  * @param {Object} config
37682  */
37683 Roo.grid.GridView = function(config){
37684     Roo.grid.GridView.superclass.constructor.call(this);
37685     this.el = null;
37686
37687     Roo.apply(this, config);
37688 };
37689
37690 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37691
37692     unselectable :  'unselectable="on"',
37693     unselectableCls :  'x-unselectable',
37694     
37695     
37696     rowClass : "x-grid-row",
37697
37698     cellClass : "x-grid-col",
37699
37700     tdClass : "x-grid-td",
37701
37702     hdClass : "x-grid-hd",
37703
37704     splitClass : "x-grid-split",
37705
37706     sortClasses : ["sort-asc", "sort-desc"],
37707
37708     enableMoveAnim : false,
37709
37710     hlColor: "C3DAF9",
37711
37712     dh : Roo.DomHelper,
37713
37714     fly : Roo.Element.fly,
37715
37716     css : Roo.util.CSS,
37717
37718     borderWidth: 1,
37719
37720     splitOffset: 3,
37721
37722     scrollIncrement : 22,
37723
37724     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37725
37726     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37727
37728     bind : function(ds, cm){
37729         if(this.ds){
37730             this.ds.un("load", this.onLoad, this);
37731             this.ds.un("datachanged", this.onDataChange, this);
37732             this.ds.un("add", this.onAdd, this);
37733             this.ds.un("remove", this.onRemove, this);
37734             this.ds.un("update", this.onUpdate, this);
37735             this.ds.un("clear", this.onClear, this);
37736         }
37737         if(ds){
37738             ds.on("load", this.onLoad, this);
37739             ds.on("datachanged", this.onDataChange, this);
37740             ds.on("add", this.onAdd, this);
37741             ds.on("remove", this.onRemove, this);
37742             ds.on("update", this.onUpdate, this);
37743             ds.on("clear", this.onClear, this);
37744         }
37745         this.ds = ds;
37746
37747         if(this.cm){
37748             this.cm.un("widthchange", this.onColWidthChange, this);
37749             this.cm.un("headerchange", this.onHeaderChange, this);
37750             this.cm.un("hiddenchange", this.onHiddenChange, this);
37751             this.cm.un("columnmoved", this.onColumnMove, this);
37752             this.cm.un("columnlockchange", this.onColumnLock, this);
37753         }
37754         if(cm){
37755             this.generateRules(cm);
37756             cm.on("widthchange", this.onColWidthChange, this);
37757             cm.on("headerchange", this.onHeaderChange, this);
37758             cm.on("hiddenchange", this.onHiddenChange, this);
37759             cm.on("columnmoved", this.onColumnMove, this);
37760             cm.on("columnlockchange", this.onColumnLock, this);
37761         }
37762         this.cm = cm;
37763     },
37764
37765     init: function(grid){
37766         Roo.grid.GridView.superclass.init.call(this, grid);
37767
37768         this.bind(grid.dataSource, grid.colModel);
37769
37770         grid.on("headerclick", this.handleHeaderClick, this);
37771
37772         if(grid.trackMouseOver){
37773             grid.on("mouseover", this.onRowOver, this);
37774             grid.on("mouseout", this.onRowOut, this);
37775         }
37776         grid.cancelTextSelection = function(){};
37777         this.gridId = grid.id;
37778
37779         var tpls = this.templates || {};
37780
37781         if(!tpls.master){
37782             tpls.master = new Roo.Template(
37783                '<div class="x-grid" hidefocus="true">',
37784                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37785                   '<div class="x-grid-topbar"></div>',
37786                   '<div class="x-grid-scroller"><div></div></div>',
37787                   '<div class="x-grid-locked">',
37788                       '<div class="x-grid-header">{lockedHeader}</div>',
37789                       '<div class="x-grid-body">{lockedBody}</div>',
37790                   "</div>",
37791                   '<div class="x-grid-viewport">',
37792                       '<div class="x-grid-header">{header}</div>',
37793                       '<div class="x-grid-body">{body}</div>',
37794                   "</div>",
37795                   '<div class="x-grid-bottombar"></div>',
37796                  
37797                   '<div class="x-grid-resize-proxy">&#160;</div>',
37798                "</div>"
37799             );
37800             tpls.master.disableformats = true;
37801         }
37802
37803         if(!tpls.header){
37804             tpls.header = new Roo.Template(
37805                '<table border="0" cellspacing="0" cellpadding="0">',
37806                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37807                "</table>{splits}"
37808             );
37809             tpls.header.disableformats = true;
37810         }
37811         tpls.header.compile();
37812
37813         if(!tpls.hcell){
37814             tpls.hcell = new Roo.Template(
37815                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37816                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37817                 "</div></td>"
37818              );
37819              tpls.hcell.disableFormats = true;
37820         }
37821         tpls.hcell.compile();
37822
37823         if(!tpls.hsplit){
37824             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37825                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37826             tpls.hsplit.disableFormats = true;
37827         }
37828         tpls.hsplit.compile();
37829
37830         if(!tpls.body){
37831             tpls.body = new Roo.Template(
37832                '<table border="0" cellspacing="0" cellpadding="0">',
37833                "<tbody>{rows}</tbody>",
37834                "</table>"
37835             );
37836             tpls.body.disableFormats = true;
37837         }
37838         tpls.body.compile();
37839
37840         if(!tpls.row){
37841             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37842             tpls.row.disableFormats = true;
37843         }
37844         tpls.row.compile();
37845
37846         if(!tpls.cell){
37847             tpls.cell = new Roo.Template(
37848                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37849                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37850                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37851                 "</td>"
37852             );
37853             tpls.cell.disableFormats = true;
37854         }
37855         tpls.cell.compile();
37856
37857         this.templates = tpls;
37858     },
37859
37860     // remap these for backwards compat
37861     onColWidthChange : function(){
37862         this.updateColumns.apply(this, arguments);
37863     },
37864     onHeaderChange : function(){
37865         this.updateHeaders.apply(this, arguments);
37866     }, 
37867     onHiddenChange : function(){
37868         this.handleHiddenChange.apply(this, arguments);
37869     },
37870     onColumnMove : function(){
37871         this.handleColumnMove.apply(this, arguments);
37872     },
37873     onColumnLock : function(){
37874         this.handleLockChange.apply(this, arguments);
37875     },
37876
37877     onDataChange : function(){
37878         this.refresh();
37879         this.updateHeaderSortState();
37880     },
37881
37882     onClear : function(){
37883         this.refresh();
37884     },
37885
37886     onUpdate : function(ds, record){
37887         this.refreshRow(record);
37888     },
37889
37890     refreshRow : function(record){
37891         var ds = this.ds, index;
37892         if(typeof record == 'number'){
37893             index = record;
37894             record = ds.getAt(index);
37895         }else{
37896             index = ds.indexOf(record);
37897         }
37898         this.insertRows(ds, index, index, true);
37899         this.onRemove(ds, record, index+1, true);
37900         this.syncRowHeights(index, index);
37901         this.layout();
37902         this.fireEvent("rowupdated", this, index, record);
37903     },
37904
37905     onAdd : function(ds, records, index){
37906         this.insertRows(ds, index, index + (records.length-1));
37907     },
37908
37909     onRemove : function(ds, record, index, isUpdate){
37910         if(isUpdate !== true){
37911             this.fireEvent("beforerowremoved", this, index, record);
37912         }
37913         var bt = this.getBodyTable(), lt = this.getLockedTable();
37914         if(bt.rows[index]){
37915             bt.firstChild.removeChild(bt.rows[index]);
37916         }
37917         if(lt.rows[index]){
37918             lt.firstChild.removeChild(lt.rows[index]);
37919         }
37920         if(isUpdate !== true){
37921             this.stripeRows(index);
37922             this.syncRowHeights(index, index);
37923             this.layout();
37924             this.fireEvent("rowremoved", this, index, record);
37925         }
37926     },
37927
37928     onLoad : function(){
37929         this.scrollToTop();
37930     },
37931
37932     /**
37933      * Scrolls the grid to the top
37934      */
37935     scrollToTop : function(){
37936         if(this.scroller){
37937             this.scroller.dom.scrollTop = 0;
37938             this.syncScroll();
37939         }
37940     },
37941
37942     /**
37943      * Gets a panel in the header of the grid that can be used for toolbars etc.
37944      * After modifying the contents of this panel a call to grid.autoSize() may be
37945      * required to register any changes in size.
37946      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37947      * @return Roo.Element
37948      */
37949     getHeaderPanel : function(doShow){
37950         if(doShow){
37951             this.headerPanel.show();
37952         }
37953         return this.headerPanel;
37954     },
37955
37956     /**
37957      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37958      * After modifying the contents of this panel a call to grid.autoSize() may be
37959      * required to register any changes in size.
37960      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37961      * @return Roo.Element
37962      */
37963     getFooterPanel : function(doShow){
37964         if(doShow){
37965             this.footerPanel.show();
37966         }
37967         return this.footerPanel;
37968     },
37969
37970     initElements : function(){
37971         var E = Roo.Element;
37972         var el = this.grid.getGridEl().dom.firstChild;
37973         var cs = el.childNodes;
37974
37975         this.el = new E(el);
37976         
37977          this.focusEl = new E(el.firstChild);
37978         this.focusEl.swallowEvent("click", true);
37979         
37980         this.headerPanel = new E(cs[1]);
37981         this.headerPanel.enableDisplayMode("block");
37982
37983         this.scroller = new E(cs[2]);
37984         this.scrollSizer = new E(this.scroller.dom.firstChild);
37985
37986         this.lockedWrap = new E(cs[3]);
37987         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37988         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37989
37990         this.mainWrap = new E(cs[4]);
37991         this.mainHd = new E(this.mainWrap.dom.firstChild);
37992         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37993
37994         this.footerPanel = new E(cs[5]);
37995         this.footerPanel.enableDisplayMode("block");
37996
37997         this.resizeProxy = new E(cs[6]);
37998
37999         this.headerSelector = String.format(
38000            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38001            this.lockedHd.id, this.mainHd.id
38002         );
38003
38004         this.splitterSelector = String.format(
38005            '#{0} div.x-grid-split, #{1} div.x-grid-split',
38006            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38007         );
38008     },
38009     idToCssName : function(s)
38010     {
38011         return s.replace(/[^a-z0-9]+/ig, '-');
38012     },
38013
38014     getHeaderCell : function(index){
38015         return Roo.DomQuery.select(this.headerSelector)[index];
38016     },
38017
38018     getHeaderCellMeasure : function(index){
38019         return this.getHeaderCell(index).firstChild;
38020     },
38021
38022     getHeaderCellText : function(index){
38023         return this.getHeaderCell(index).firstChild.firstChild;
38024     },
38025
38026     getLockedTable : function(){
38027         return this.lockedBody.dom.firstChild;
38028     },
38029
38030     getBodyTable : function(){
38031         return this.mainBody.dom.firstChild;
38032     },
38033
38034     getLockedRow : function(index){
38035         return this.getLockedTable().rows[index];
38036     },
38037
38038     getRow : function(index){
38039         return this.getBodyTable().rows[index];
38040     },
38041
38042     getRowComposite : function(index){
38043         if(!this.rowEl){
38044             this.rowEl = new Roo.CompositeElementLite();
38045         }
38046         var els = [], lrow, mrow;
38047         if(lrow = this.getLockedRow(index)){
38048             els.push(lrow);
38049         }
38050         if(mrow = this.getRow(index)){
38051             els.push(mrow);
38052         }
38053         this.rowEl.elements = els;
38054         return this.rowEl;
38055     },
38056     /**
38057      * Gets the 'td' of the cell
38058      * 
38059      * @param {Integer} rowIndex row to select
38060      * @param {Integer} colIndex column to select
38061      * 
38062      * @return {Object} 
38063      */
38064     getCell : function(rowIndex, colIndex){
38065         var locked = this.cm.getLockedCount();
38066         var source;
38067         if(colIndex < locked){
38068             source = this.lockedBody.dom.firstChild;
38069         }else{
38070             source = this.mainBody.dom.firstChild;
38071             colIndex -= locked;
38072         }
38073         return source.rows[rowIndex].childNodes[colIndex];
38074     },
38075
38076     getCellText : function(rowIndex, colIndex){
38077         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38078     },
38079
38080     getCellBox : function(cell){
38081         var b = this.fly(cell).getBox();
38082         if(Roo.isOpera){ // opera fails to report the Y
38083             b.y = cell.offsetTop + this.mainBody.getY();
38084         }
38085         return b;
38086     },
38087
38088     getCellIndex : function(cell){
38089         var id = String(cell.className).match(this.cellRE);
38090         if(id){
38091             return parseInt(id[1], 10);
38092         }
38093         return 0;
38094     },
38095
38096     findHeaderIndex : function(n){
38097         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38098         return r ? this.getCellIndex(r) : false;
38099     },
38100
38101     findHeaderCell : function(n){
38102         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38103         return r ? r : false;
38104     },
38105
38106     findRowIndex : function(n){
38107         if(!n){
38108             return false;
38109         }
38110         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38111         return r ? r.rowIndex : false;
38112     },
38113
38114     findCellIndex : function(node){
38115         var stop = this.el.dom;
38116         while(node && node != stop){
38117             if(this.findRE.test(node.className)){
38118                 return this.getCellIndex(node);
38119             }
38120             node = node.parentNode;
38121         }
38122         return false;
38123     },
38124
38125     getColumnId : function(index){
38126         return this.cm.getColumnId(index);
38127     },
38128
38129     getSplitters : function()
38130     {
38131         if(this.splitterSelector){
38132            return Roo.DomQuery.select(this.splitterSelector);
38133         }else{
38134             return null;
38135       }
38136     },
38137
38138     getSplitter : function(index){
38139         return this.getSplitters()[index];
38140     },
38141
38142     onRowOver : function(e, t){
38143         var row;
38144         if((row = this.findRowIndex(t)) !== false){
38145             this.getRowComposite(row).addClass("x-grid-row-over");
38146         }
38147     },
38148
38149     onRowOut : function(e, t){
38150         var row;
38151         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38152             this.getRowComposite(row).removeClass("x-grid-row-over");
38153         }
38154     },
38155
38156     renderHeaders : function(){
38157         var cm = this.cm;
38158         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38159         var cb = [], lb = [], sb = [], lsb = [], p = {};
38160         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38161             p.cellId = "x-grid-hd-0-" + i;
38162             p.splitId = "x-grid-csplit-0-" + i;
38163             p.id = cm.getColumnId(i);
38164             p.value = cm.getColumnHeader(i) || "";
38165             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
38166             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38167             if(!cm.isLocked(i)){
38168                 cb[cb.length] = ct.apply(p);
38169                 sb[sb.length] = st.apply(p);
38170             }else{
38171                 lb[lb.length] = ct.apply(p);
38172                 lsb[lsb.length] = st.apply(p);
38173             }
38174         }
38175         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38176                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38177     },
38178
38179     updateHeaders : function(){
38180         var html = this.renderHeaders();
38181         this.lockedHd.update(html[0]);
38182         this.mainHd.update(html[1]);
38183     },
38184
38185     /**
38186      * Focuses the specified row.
38187      * @param {Number} row The row index
38188      */
38189     focusRow : function(row)
38190     {
38191         //Roo.log('GridView.focusRow');
38192         var x = this.scroller.dom.scrollLeft;
38193         this.focusCell(row, 0, false);
38194         this.scroller.dom.scrollLeft = x;
38195     },
38196
38197     /**
38198      * Focuses the specified cell.
38199      * @param {Number} row The row index
38200      * @param {Number} col The column index
38201      * @param {Boolean} hscroll false to disable horizontal scrolling
38202      */
38203     focusCell : function(row, col, hscroll)
38204     {
38205         //Roo.log('GridView.focusCell');
38206         var el = this.ensureVisible(row, col, hscroll);
38207         this.focusEl.alignTo(el, "tl-tl");
38208         if(Roo.isGecko){
38209             this.focusEl.focus();
38210         }else{
38211             this.focusEl.focus.defer(1, this.focusEl);
38212         }
38213     },
38214
38215     /**
38216      * Scrolls the specified cell into view
38217      * @param {Number} row The row index
38218      * @param {Number} col The column index
38219      * @param {Boolean} hscroll false to disable horizontal scrolling
38220      */
38221     ensureVisible : function(row, col, hscroll)
38222     {
38223         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38224         //return null; //disable for testing.
38225         if(typeof row != "number"){
38226             row = row.rowIndex;
38227         }
38228         if(row < 0 && row >= this.ds.getCount()){
38229             return  null;
38230         }
38231         col = (col !== undefined ? col : 0);
38232         var cm = this.grid.colModel;
38233         while(cm.isHidden(col)){
38234             col++;
38235         }
38236
38237         var el = this.getCell(row, col);
38238         if(!el){
38239             return null;
38240         }
38241         var c = this.scroller.dom;
38242
38243         var ctop = parseInt(el.offsetTop, 10);
38244         var cleft = parseInt(el.offsetLeft, 10);
38245         var cbot = ctop + el.offsetHeight;
38246         var cright = cleft + el.offsetWidth;
38247         
38248         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38249         var stop = parseInt(c.scrollTop, 10);
38250         var sleft = parseInt(c.scrollLeft, 10);
38251         var sbot = stop + ch;
38252         var sright = sleft + c.clientWidth;
38253         /*
38254         Roo.log('GridView.ensureVisible:' +
38255                 ' ctop:' + ctop +
38256                 ' c.clientHeight:' + c.clientHeight +
38257                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38258                 ' stop:' + stop +
38259                 ' cbot:' + cbot +
38260                 ' sbot:' + sbot +
38261                 ' ch:' + ch  
38262                 );
38263         */
38264         if(ctop < stop){
38265             c.scrollTop = ctop;
38266             //Roo.log("set scrolltop to ctop DISABLE?");
38267         }else if(cbot > sbot){
38268             //Roo.log("set scrolltop to cbot-ch");
38269             c.scrollTop = cbot-ch;
38270         }
38271         
38272         if(hscroll !== false){
38273             if(cleft < sleft){
38274                 c.scrollLeft = cleft;
38275             }else if(cright > sright){
38276                 c.scrollLeft = cright-c.clientWidth;
38277             }
38278         }
38279          
38280         return el;
38281     },
38282
38283     updateColumns : function(){
38284         this.grid.stopEditing();
38285         var cm = this.grid.colModel, colIds = this.getColumnIds();
38286         //var totalWidth = cm.getTotalWidth();
38287         var pos = 0;
38288         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38289             //if(cm.isHidden(i)) continue;
38290             var w = cm.getColumnWidth(i);
38291             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38292             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38293         }
38294         this.updateSplitters();
38295     },
38296
38297     generateRules : function(cm){
38298         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38299         Roo.util.CSS.removeStyleSheet(rulesId);
38300         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38301             var cid = cm.getColumnId(i);
38302             var align = '';
38303             if(cm.config[i].align){
38304                 align = 'text-align:'+cm.config[i].align+';';
38305             }
38306             var hidden = '';
38307             if(cm.isHidden(i)){
38308                 hidden = 'display:none;';
38309             }
38310             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38311             ruleBuf.push(
38312                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38313                     this.hdSelector, cid, " {\n", align, width, "}\n",
38314                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
38315                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
38316         }
38317         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38318     },
38319
38320     updateSplitters : function(){
38321         var cm = this.cm, s = this.getSplitters();
38322         if(s){ // splitters not created yet
38323             var pos = 0, locked = true;
38324             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38325                 if(cm.isHidden(i)) {
38326                     continue;
38327                 }
38328                 var w = cm.getColumnWidth(i); // make sure it's a number
38329                 if(!cm.isLocked(i) && locked){
38330                     pos = 0;
38331                     locked = false;
38332                 }
38333                 pos += w;
38334                 s[i].style.left = (pos-this.splitOffset) + "px";
38335             }
38336         }
38337     },
38338
38339     handleHiddenChange : function(colModel, colIndex, hidden){
38340         if(hidden){
38341             this.hideColumn(colIndex);
38342         }else{
38343             this.unhideColumn(colIndex);
38344         }
38345     },
38346
38347     hideColumn : function(colIndex){
38348         var cid = this.getColumnId(colIndex);
38349         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38350         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38351         if(Roo.isSafari){
38352             this.updateHeaders();
38353         }
38354         this.updateSplitters();
38355         this.layout();
38356     },
38357
38358     unhideColumn : function(colIndex){
38359         var cid = this.getColumnId(colIndex);
38360         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38361         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38362
38363         if(Roo.isSafari){
38364             this.updateHeaders();
38365         }
38366         this.updateSplitters();
38367         this.layout();
38368     },
38369
38370     insertRows : function(dm, firstRow, lastRow, isUpdate){
38371         if(firstRow == 0 && lastRow == dm.getCount()-1){
38372             this.refresh();
38373         }else{
38374             if(!isUpdate){
38375                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38376             }
38377             var s = this.getScrollState();
38378             var markup = this.renderRows(firstRow, lastRow);
38379             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38380             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38381             this.restoreScroll(s);
38382             if(!isUpdate){
38383                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38384                 this.syncRowHeights(firstRow, lastRow);
38385                 this.stripeRows(firstRow);
38386                 this.layout();
38387             }
38388         }
38389     },
38390
38391     bufferRows : function(markup, target, index){
38392         var before = null, trows = target.rows, tbody = target.tBodies[0];
38393         if(index < trows.length){
38394             before = trows[index];
38395         }
38396         var b = document.createElement("div");
38397         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38398         var rows = b.firstChild.rows;
38399         for(var i = 0, len = rows.length; i < len; i++){
38400             if(before){
38401                 tbody.insertBefore(rows[0], before);
38402             }else{
38403                 tbody.appendChild(rows[0]);
38404             }
38405         }
38406         b.innerHTML = "";
38407         b = null;
38408     },
38409
38410     deleteRows : function(dm, firstRow, lastRow){
38411         if(dm.getRowCount()<1){
38412             this.fireEvent("beforerefresh", this);
38413             this.mainBody.update("");
38414             this.lockedBody.update("");
38415             this.fireEvent("refresh", this);
38416         }else{
38417             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38418             var bt = this.getBodyTable();
38419             var tbody = bt.firstChild;
38420             var rows = bt.rows;
38421             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38422                 tbody.removeChild(rows[firstRow]);
38423             }
38424             this.stripeRows(firstRow);
38425             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38426         }
38427     },
38428
38429     updateRows : function(dataSource, firstRow, lastRow){
38430         var s = this.getScrollState();
38431         this.refresh();
38432         this.restoreScroll(s);
38433     },
38434
38435     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38436         if(!noRefresh){
38437            this.refresh();
38438         }
38439         this.updateHeaderSortState();
38440     },
38441
38442     getScrollState : function(){
38443         
38444         var sb = this.scroller.dom;
38445         return {left: sb.scrollLeft, top: sb.scrollTop};
38446     },
38447
38448     stripeRows : function(startRow){
38449         if(!this.grid.stripeRows || this.ds.getCount() < 1){
38450             return;
38451         }
38452         startRow = startRow || 0;
38453         var rows = this.getBodyTable().rows;
38454         var lrows = this.getLockedTable().rows;
38455         var cls = ' x-grid-row-alt ';
38456         for(var i = startRow, len = rows.length; i < len; i++){
38457             var row = rows[i], lrow = lrows[i];
38458             var isAlt = ((i+1) % 2 == 0);
38459             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38460             if(isAlt == hasAlt){
38461                 continue;
38462             }
38463             if(isAlt){
38464                 row.className += " x-grid-row-alt";
38465             }else{
38466                 row.className = row.className.replace("x-grid-row-alt", "");
38467             }
38468             if(lrow){
38469                 lrow.className = row.className;
38470             }
38471         }
38472     },
38473
38474     restoreScroll : function(state){
38475         //Roo.log('GridView.restoreScroll');
38476         var sb = this.scroller.dom;
38477         sb.scrollLeft = state.left;
38478         sb.scrollTop = state.top;
38479         this.syncScroll();
38480     },
38481
38482     syncScroll : function(){
38483         //Roo.log('GridView.syncScroll');
38484         var sb = this.scroller.dom;
38485         var sh = this.mainHd.dom;
38486         var bs = this.mainBody.dom;
38487         var lv = this.lockedBody.dom;
38488         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38489         lv.scrollTop = bs.scrollTop = sb.scrollTop;
38490     },
38491
38492     handleScroll : function(e){
38493         this.syncScroll();
38494         var sb = this.scroller.dom;
38495         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38496         e.stopEvent();
38497     },
38498
38499     handleWheel : function(e){
38500         var d = e.getWheelDelta();
38501         this.scroller.dom.scrollTop -= d*22;
38502         // set this here to prevent jumpy scrolling on large tables
38503         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38504         e.stopEvent();
38505     },
38506
38507     renderRows : function(startRow, endRow){
38508         // pull in all the crap needed to render rows
38509         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38510         var colCount = cm.getColumnCount();
38511
38512         if(ds.getCount() < 1){
38513             return ["", ""];
38514         }
38515
38516         // build a map for all the columns
38517         var cs = [];
38518         for(var i = 0; i < colCount; i++){
38519             var name = cm.getDataIndex(i);
38520             cs[i] = {
38521                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38522                 renderer : cm.getRenderer(i),
38523                 id : cm.getColumnId(i),
38524                 locked : cm.isLocked(i),
38525                 has_editor : cm.isCellEditable(i)
38526             };
38527         }
38528
38529         startRow = startRow || 0;
38530         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38531
38532         // records to render
38533         var rs = ds.getRange(startRow, endRow);
38534
38535         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38536     },
38537
38538     // As much as I hate to duplicate code, this was branched because FireFox really hates
38539     // [].join("") on strings. The performance difference was substantial enough to
38540     // branch this function
38541     doRender : Roo.isGecko ?
38542             function(cs, rs, ds, startRow, colCount, stripe){
38543                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38544                 // buffers
38545                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38546                 
38547                 var hasListener = this.grid.hasListener('rowclass');
38548                 var rowcfg = {};
38549                 for(var j = 0, len = rs.length; j < len; j++){
38550                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38551                     for(var i = 0; i < colCount; i++){
38552                         c = cs[i];
38553                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38554                         p.id = c.id;
38555                         p.css = p.attr = "";
38556                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38557                         if(p.value == undefined || p.value === "") {
38558                             p.value = "&#160;";
38559                         }
38560                         if(c.has_editor){
38561                             p.css += ' x-grid-editable-cell';
38562                         }
38563                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38564                             p.css +=  ' x-grid-dirty-cell';
38565                         }
38566                         var markup = ct.apply(p);
38567                         if(!c.locked){
38568                             cb+= markup;
38569                         }else{
38570                             lcb+= markup;
38571                         }
38572                     }
38573                     var alt = [];
38574                     if(stripe && ((rowIndex+1) % 2 == 0)){
38575                         alt.push("x-grid-row-alt")
38576                     }
38577                     if(r.dirty){
38578                         alt.push(  " x-grid-dirty-row");
38579                     }
38580                     rp.cells = lcb;
38581                     if(this.getRowClass){
38582                         alt.push(this.getRowClass(r, rowIndex));
38583                     }
38584                     if (hasListener) {
38585                         rowcfg = {
38586                              
38587                             record: r,
38588                             rowIndex : rowIndex,
38589                             rowClass : ''
38590                         };
38591                         this.grid.fireEvent('rowclass', this, rowcfg);
38592                         alt.push(rowcfg.rowClass);
38593                     }
38594                     rp.alt = alt.join(" ");
38595                     lbuf+= rt.apply(rp);
38596                     rp.cells = cb;
38597                     buf+=  rt.apply(rp);
38598                 }
38599                 return [lbuf, buf];
38600             } :
38601             function(cs, rs, ds, startRow, colCount, stripe){
38602                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38603                 // buffers
38604                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38605                 var hasListener = this.grid.hasListener('rowclass');
38606  
38607                 var rowcfg = {};
38608                 for(var j = 0, len = rs.length; j < len; j++){
38609                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38610                     for(var i = 0; i < colCount; i++){
38611                         c = cs[i];
38612                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38613                         p.id = c.id;
38614                         p.css = p.attr = "";
38615                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38616                         if(p.value == undefined || p.value === "") {
38617                             p.value = "&#160;";
38618                         }
38619                         //Roo.log(c);
38620                          if(c.has_editor){
38621                             p.css += ' x-grid-editable-cell';
38622                         }
38623                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38624                             p.css += ' x-grid-dirty-cell' 
38625                         }
38626                         
38627                         var markup = ct.apply(p);
38628                         if(!c.locked){
38629                             cb[cb.length] = markup;
38630                         }else{
38631                             lcb[lcb.length] = markup;
38632                         }
38633                     }
38634                     var alt = [];
38635                     if(stripe && ((rowIndex+1) % 2 == 0)){
38636                         alt.push( "x-grid-row-alt");
38637                     }
38638                     if(r.dirty){
38639                         alt.push(" x-grid-dirty-row");
38640                     }
38641                     rp.cells = lcb;
38642                     if(this.getRowClass){
38643                         alt.push( this.getRowClass(r, rowIndex));
38644                     }
38645                     if (hasListener) {
38646                         rowcfg = {
38647                              
38648                             record: r,
38649                             rowIndex : rowIndex,
38650                             rowClass : ''
38651                         };
38652                         this.grid.fireEvent('rowclass', this, rowcfg);
38653                         alt.push(rowcfg.rowClass);
38654                     }
38655                     
38656                     rp.alt = alt.join(" ");
38657                     rp.cells = lcb.join("");
38658                     lbuf[lbuf.length] = rt.apply(rp);
38659                     rp.cells = cb.join("");
38660                     buf[buf.length] =  rt.apply(rp);
38661                 }
38662                 return [lbuf.join(""), buf.join("")];
38663             },
38664
38665     renderBody : function(){
38666         var markup = this.renderRows();
38667         var bt = this.templates.body;
38668         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38669     },
38670
38671     /**
38672      * Refreshes the grid
38673      * @param {Boolean} headersToo
38674      */
38675     refresh : function(headersToo){
38676         this.fireEvent("beforerefresh", this);
38677         this.grid.stopEditing();
38678         var result = this.renderBody();
38679         this.lockedBody.update(result[0]);
38680         this.mainBody.update(result[1]);
38681         if(headersToo === true){
38682             this.updateHeaders();
38683             this.updateColumns();
38684             this.updateSplitters();
38685             this.updateHeaderSortState();
38686         }
38687         this.syncRowHeights();
38688         this.layout();
38689         this.fireEvent("refresh", this);
38690     },
38691
38692     handleColumnMove : function(cm, oldIndex, newIndex){
38693         this.indexMap = null;
38694         var s = this.getScrollState();
38695         this.refresh(true);
38696         this.restoreScroll(s);
38697         this.afterMove(newIndex);
38698     },
38699
38700     afterMove : function(colIndex){
38701         if(this.enableMoveAnim && Roo.enableFx){
38702             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38703         }
38704         // if multisort - fix sortOrder, and reload..
38705         if (this.grid.dataSource.multiSort) {
38706             // the we can call sort again..
38707             var dm = this.grid.dataSource;
38708             var cm = this.grid.colModel;
38709             var so = [];
38710             for(var i = 0; i < cm.config.length; i++ ) {
38711                 
38712                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38713                     continue; // dont' bother, it's not in sort list or being set.
38714                 }
38715                 
38716                 so.push(cm.config[i].dataIndex);
38717             };
38718             dm.sortOrder = so;
38719             dm.load(dm.lastOptions);
38720             
38721             
38722         }
38723         
38724     },
38725
38726     updateCell : function(dm, rowIndex, dataIndex){
38727         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38728         if(typeof colIndex == "undefined"){ // not present in grid
38729             return;
38730         }
38731         var cm = this.grid.colModel;
38732         var cell = this.getCell(rowIndex, colIndex);
38733         var cellText = this.getCellText(rowIndex, colIndex);
38734
38735         var p = {
38736             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38737             id : cm.getColumnId(colIndex),
38738             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38739         };
38740         var renderer = cm.getRenderer(colIndex);
38741         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38742         if(typeof val == "undefined" || val === "") {
38743             val = "&#160;";
38744         }
38745         cellText.innerHTML = val;
38746         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38747         this.syncRowHeights(rowIndex, rowIndex);
38748     },
38749
38750     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38751         var maxWidth = 0;
38752         if(this.grid.autoSizeHeaders){
38753             var h = this.getHeaderCellMeasure(colIndex);
38754             maxWidth = Math.max(maxWidth, h.scrollWidth);
38755         }
38756         var tb, index;
38757         if(this.cm.isLocked(colIndex)){
38758             tb = this.getLockedTable();
38759             index = colIndex;
38760         }else{
38761             tb = this.getBodyTable();
38762             index = colIndex - this.cm.getLockedCount();
38763         }
38764         if(tb && tb.rows){
38765             var rows = tb.rows;
38766             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38767             for(var i = 0; i < stopIndex; i++){
38768                 var cell = rows[i].childNodes[index].firstChild;
38769                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38770             }
38771         }
38772         return maxWidth + /*margin for error in IE*/ 5;
38773     },
38774     /**
38775      * Autofit a column to its content.
38776      * @param {Number} colIndex
38777      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38778      */
38779      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38780          if(this.cm.isHidden(colIndex)){
38781              return; // can't calc a hidden column
38782          }
38783         if(forceMinSize){
38784             var cid = this.cm.getColumnId(colIndex);
38785             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38786            if(this.grid.autoSizeHeaders){
38787                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38788            }
38789         }
38790         var newWidth = this.calcColumnWidth(colIndex);
38791         this.cm.setColumnWidth(colIndex,
38792             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38793         if(!suppressEvent){
38794             this.grid.fireEvent("columnresize", colIndex, newWidth);
38795         }
38796     },
38797
38798     /**
38799      * Autofits all columns to their content and then expands to fit any extra space in the grid
38800      */
38801      autoSizeColumns : function(){
38802         var cm = this.grid.colModel;
38803         var colCount = cm.getColumnCount();
38804         for(var i = 0; i < colCount; i++){
38805             this.autoSizeColumn(i, true, true);
38806         }
38807         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38808             this.fitColumns();
38809         }else{
38810             this.updateColumns();
38811             this.layout();
38812         }
38813     },
38814
38815     /**
38816      * Autofits all columns to the grid's width proportionate with their current size
38817      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38818      */
38819     fitColumns : function(reserveScrollSpace){
38820         var cm = this.grid.colModel;
38821         var colCount = cm.getColumnCount();
38822         var cols = [];
38823         var width = 0;
38824         var i, w;
38825         for (i = 0; i < colCount; i++){
38826             if(!cm.isHidden(i) && !cm.isFixed(i)){
38827                 w = cm.getColumnWidth(i);
38828                 cols.push(i);
38829                 cols.push(w);
38830                 width += w;
38831             }
38832         }
38833         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38834         if(reserveScrollSpace){
38835             avail -= 17;
38836         }
38837         var frac = (avail - cm.getTotalWidth())/width;
38838         while (cols.length){
38839             w = cols.pop();
38840             i = cols.pop();
38841             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38842         }
38843         this.updateColumns();
38844         this.layout();
38845     },
38846
38847     onRowSelect : function(rowIndex){
38848         var row = this.getRowComposite(rowIndex);
38849         row.addClass("x-grid-row-selected");
38850     },
38851
38852     onRowDeselect : function(rowIndex){
38853         var row = this.getRowComposite(rowIndex);
38854         row.removeClass("x-grid-row-selected");
38855     },
38856
38857     onCellSelect : function(row, col){
38858         var cell = this.getCell(row, col);
38859         if(cell){
38860             Roo.fly(cell).addClass("x-grid-cell-selected");
38861         }
38862     },
38863
38864     onCellDeselect : function(row, col){
38865         var cell = this.getCell(row, col);
38866         if(cell){
38867             Roo.fly(cell).removeClass("x-grid-cell-selected");
38868         }
38869     },
38870
38871     updateHeaderSortState : function(){
38872         
38873         // sort state can be single { field: xxx, direction : yyy}
38874         // or   { xxx=>ASC , yyy : DESC ..... }
38875         
38876         var mstate = {};
38877         if (!this.ds.multiSort) { 
38878             var state = this.ds.getSortState();
38879             if(!state){
38880                 return;
38881             }
38882             mstate[state.field] = state.direction;
38883             // FIXME... - this is not used here.. but might be elsewhere..
38884             this.sortState = state;
38885             
38886         } else {
38887             mstate = this.ds.sortToggle;
38888         }
38889         //remove existing sort classes..
38890         
38891         var sc = this.sortClasses;
38892         var hds = this.el.select(this.headerSelector).removeClass(sc);
38893         
38894         for(var f in mstate) {
38895         
38896             var sortColumn = this.cm.findColumnIndex(f);
38897             
38898             if(sortColumn != -1){
38899                 var sortDir = mstate[f];        
38900                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38901             }
38902         }
38903         
38904          
38905         
38906     },
38907
38908
38909     handleHeaderClick : function(g, index,e){
38910         
38911         Roo.log("header click");
38912         
38913         if (Roo.isTouch) {
38914             // touch events on header are handled by context
38915             this.handleHdCtx(g,index,e);
38916             return;
38917         }
38918         
38919         
38920         if(this.headersDisabled){
38921             return;
38922         }
38923         var dm = g.dataSource, cm = g.colModel;
38924         if(!cm.isSortable(index)){
38925             return;
38926         }
38927         g.stopEditing();
38928         
38929         if (dm.multiSort) {
38930             // update the sortOrder
38931             var so = [];
38932             for(var i = 0; i < cm.config.length; i++ ) {
38933                 
38934                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38935                     continue; // dont' bother, it's not in sort list or being set.
38936                 }
38937                 
38938                 so.push(cm.config[i].dataIndex);
38939             };
38940             dm.sortOrder = so;
38941         }
38942         
38943         
38944         dm.sort(cm.getDataIndex(index));
38945     },
38946
38947
38948     destroy : function(){
38949         if(this.colMenu){
38950             this.colMenu.removeAll();
38951             Roo.menu.MenuMgr.unregister(this.colMenu);
38952             this.colMenu.getEl().remove();
38953             delete this.colMenu;
38954         }
38955         if(this.hmenu){
38956             this.hmenu.removeAll();
38957             Roo.menu.MenuMgr.unregister(this.hmenu);
38958             this.hmenu.getEl().remove();
38959             delete this.hmenu;
38960         }
38961         if(this.grid.enableColumnMove){
38962             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38963             if(dds){
38964                 for(var dd in dds){
38965                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38966                         var elid = dds[dd].dragElId;
38967                         dds[dd].unreg();
38968                         Roo.get(elid).remove();
38969                     } else if(dds[dd].config.isTarget){
38970                         dds[dd].proxyTop.remove();
38971                         dds[dd].proxyBottom.remove();
38972                         dds[dd].unreg();
38973                     }
38974                     if(Roo.dd.DDM.locationCache[dd]){
38975                         delete Roo.dd.DDM.locationCache[dd];
38976                     }
38977                 }
38978                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38979             }
38980         }
38981         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38982         this.bind(null, null);
38983         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38984     },
38985
38986     handleLockChange : function(){
38987         this.refresh(true);
38988     },
38989
38990     onDenyColumnLock : function(){
38991
38992     },
38993
38994     onDenyColumnHide : function(){
38995
38996     },
38997
38998     handleHdMenuClick : function(item){
38999         var index = this.hdCtxIndex;
39000         var cm = this.cm, ds = this.ds;
39001         switch(item.id){
39002             case "asc":
39003                 ds.sort(cm.getDataIndex(index), "ASC");
39004                 break;
39005             case "desc":
39006                 ds.sort(cm.getDataIndex(index), "DESC");
39007                 break;
39008             case "lock":
39009                 var lc = cm.getLockedCount();
39010                 if(cm.getColumnCount(true) <= lc+1){
39011                     this.onDenyColumnLock();
39012                     return;
39013                 }
39014                 if(lc != index){
39015                     cm.setLocked(index, true, true);
39016                     cm.moveColumn(index, lc);
39017                     this.grid.fireEvent("columnmove", index, lc);
39018                 }else{
39019                     cm.setLocked(index, true);
39020                 }
39021             break;
39022             case "unlock":
39023                 var lc = cm.getLockedCount();
39024                 if((lc-1) != index){
39025                     cm.setLocked(index, false, true);
39026                     cm.moveColumn(index, lc-1);
39027                     this.grid.fireEvent("columnmove", index, lc-1);
39028                 }else{
39029                     cm.setLocked(index, false);
39030                 }
39031             break;
39032             case 'wider': // used to expand cols on touch..
39033             case 'narrow':
39034                 var cw = cm.getColumnWidth(index);
39035                 cw += (item.id == 'wider' ? 1 : -1) * 50;
39036                 cw = Math.max(0, cw);
39037                 cw = Math.min(cw,4000);
39038                 cm.setColumnWidth(index, cw);
39039                 break;
39040                 
39041             default:
39042                 index = cm.getIndexById(item.id.substr(4));
39043                 if(index != -1){
39044                     if(item.checked && cm.getColumnCount(true) <= 1){
39045                         this.onDenyColumnHide();
39046                         return false;
39047                     }
39048                     cm.setHidden(index, item.checked);
39049                 }
39050         }
39051         return true;
39052     },
39053
39054     beforeColMenuShow : function(){
39055         var cm = this.cm,  colCount = cm.getColumnCount();
39056         this.colMenu.removeAll();
39057         
39058         var items = [];
39059         for(var i = 0; i < colCount; i++){
39060             items.push({
39061                 id: "col-"+cm.getColumnId(i),
39062                 text: cm.getColumnHeader(i),
39063                 checked: !cm.isHidden(i),
39064                 hideOnClick:false
39065             });
39066         }
39067         
39068         if (this.grid.sortColMenu) {
39069             items.sort(function(a,b) {
39070                 if (a.text == b.text) {
39071                     return 0;
39072                 }
39073                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39074             });
39075         }
39076         
39077         for(var i = 0; i < colCount; i++){
39078             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39079         }
39080     },
39081
39082     handleHdCtx : function(g, index, e){
39083         e.stopEvent();
39084         var hd = this.getHeaderCell(index);
39085         this.hdCtxIndex = index;
39086         var ms = this.hmenu.items, cm = this.cm;
39087         ms.get("asc").setDisabled(!cm.isSortable(index));
39088         ms.get("desc").setDisabled(!cm.isSortable(index));
39089         if(this.grid.enableColLock !== false){
39090             ms.get("lock").setDisabled(cm.isLocked(index));
39091             ms.get("unlock").setDisabled(!cm.isLocked(index));
39092         }
39093         this.hmenu.show(hd, "tl-bl");
39094     },
39095
39096     handleHdOver : function(e){
39097         var hd = this.findHeaderCell(e.getTarget());
39098         if(hd && !this.headersDisabled){
39099             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39100                this.fly(hd).addClass("x-grid-hd-over");
39101             }
39102         }
39103     },
39104
39105     handleHdOut : function(e){
39106         var hd = this.findHeaderCell(e.getTarget());
39107         if(hd){
39108             this.fly(hd).removeClass("x-grid-hd-over");
39109         }
39110     },
39111
39112     handleSplitDblClick : function(e, t){
39113         var i = this.getCellIndex(t);
39114         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39115             this.autoSizeColumn(i, true);
39116             this.layout();
39117         }
39118     },
39119
39120     render : function(){
39121
39122         var cm = this.cm;
39123         var colCount = cm.getColumnCount();
39124
39125         if(this.grid.monitorWindowResize === true){
39126             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39127         }
39128         var header = this.renderHeaders();
39129         var body = this.templates.body.apply({rows:""});
39130         var html = this.templates.master.apply({
39131             lockedBody: body,
39132             body: body,
39133             lockedHeader: header[0],
39134             header: header[1]
39135         });
39136
39137         //this.updateColumns();
39138
39139         this.grid.getGridEl().dom.innerHTML = html;
39140
39141         this.initElements();
39142         
39143         // a kludge to fix the random scolling effect in webkit
39144         this.el.on("scroll", function() {
39145             this.el.dom.scrollTop=0; // hopefully not recursive..
39146         },this);
39147
39148         this.scroller.on("scroll", this.handleScroll, this);
39149         this.lockedBody.on("mousewheel", this.handleWheel, this);
39150         this.mainBody.on("mousewheel", this.handleWheel, this);
39151
39152         this.mainHd.on("mouseover", this.handleHdOver, this);
39153         this.mainHd.on("mouseout", this.handleHdOut, this);
39154         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39155                 {delegate: "."+this.splitClass});
39156
39157         this.lockedHd.on("mouseover", this.handleHdOver, this);
39158         this.lockedHd.on("mouseout", this.handleHdOut, this);
39159         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39160                 {delegate: "."+this.splitClass});
39161
39162         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39163             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39164         }
39165
39166         this.updateSplitters();
39167
39168         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39169             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39170             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39171         }
39172
39173         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39174             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39175             this.hmenu.add(
39176                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39177                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39178             );
39179             if(this.grid.enableColLock !== false){
39180                 this.hmenu.add('-',
39181                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39182                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39183                 );
39184             }
39185             if (Roo.isTouch) {
39186                  this.hmenu.add('-',
39187                     {id:"wider", text: this.columnsWiderText},
39188                     {id:"narrow", text: this.columnsNarrowText }
39189                 );
39190                 
39191                  
39192             }
39193             
39194             if(this.grid.enableColumnHide !== false){
39195
39196                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39197                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39198                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39199
39200                 this.hmenu.add('-',
39201                     {id:"columns", text: this.columnsText, menu: this.colMenu}
39202                 );
39203             }
39204             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39205
39206             this.grid.on("headercontextmenu", this.handleHdCtx, this);
39207         }
39208
39209         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39210             this.dd = new Roo.grid.GridDragZone(this.grid, {
39211                 ddGroup : this.grid.ddGroup || 'GridDD'
39212             });
39213             
39214         }
39215
39216         /*
39217         for(var i = 0; i < colCount; i++){
39218             if(cm.isHidden(i)){
39219                 this.hideColumn(i);
39220             }
39221             if(cm.config[i].align){
39222                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39223                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39224             }
39225         }*/
39226         
39227         this.updateHeaderSortState();
39228
39229         this.beforeInitialResize();
39230         this.layout(true);
39231
39232         // two part rendering gives faster view to the user
39233         this.renderPhase2.defer(1, this);
39234     },
39235
39236     renderPhase2 : function(){
39237         // render the rows now
39238         this.refresh();
39239         if(this.grid.autoSizeColumns){
39240             this.autoSizeColumns();
39241         }
39242     },
39243
39244     beforeInitialResize : function(){
39245
39246     },
39247
39248     onColumnSplitterMoved : function(i, w){
39249         this.userResized = true;
39250         var cm = this.grid.colModel;
39251         cm.setColumnWidth(i, w, true);
39252         var cid = cm.getColumnId(i);
39253         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39254         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39255         this.updateSplitters();
39256         this.layout();
39257         this.grid.fireEvent("columnresize", i, w);
39258     },
39259
39260     syncRowHeights : function(startIndex, endIndex){
39261         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39262             startIndex = startIndex || 0;
39263             var mrows = this.getBodyTable().rows;
39264             var lrows = this.getLockedTable().rows;
39265             var len = mrows.length-1;
39266             endIndex = Math.min(endIndex || len, len);
39267             for(var i = startIndex; i <= endIndex; i++){
39268                 var m = mrows[i], l = lrows[i];
39269                 var h = Math.max(m.offsetHeight, l.offsetHeight);
39270                 m.style.height = l.style.height = h + "px";
39271             }
39272         }
39273     },
39274
39275     layout : function(initialRender, is2ndPass)
39276     {
39277         var g = this.grid;
39278         var auto = g.autoHeight;
39279         var scrollOffset = 16;
39280         var c = g.getGridEl(), cm = this.cm,
39281                 expandCol = g.autoExpandColumn,
39282                 gv = this;
39283         //c.beginMeasure();
39284
39285         if(!c.dom.offsetWidth){ // display:none?
39286             if(initialRender){
39287                 this.lockedWrap.show();
39288                 this.mainWrap.show();
39289             }
39290             return;
39291         }
39292
39293         var hasLock = this.cm.isLocked(0);
39294
39295         var tbh = this.headerPanel.getHeight();
39296         var bbh = this.footerPanel.getHeight();
39297
39298         if(auto){
39299             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39300             var newHeight = ch + c.getBorderWidth("tb");
39301             if(g.maxHeight){
39302                 newHeight = Math.min(g.maxHeight, newHeight);
39303             }
39304             c.setHeight(newHeight);
39305         }
39306
39307         if(g.autoWidth){
39308             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39309         }
39310
39311         var s = this.scroller;
39312
39313         var csize = c.getSize(true);
39314
39315         this.el.setSize(csize.width, csize.height);
39316
39317         this.headerPanel.setWidth(csize.width);
39318         this.footerPanel.setWidth(csize.width);
39319
39320         var hdHeight = this.mainHd.getHeight();
39321         var vw = csize.width;
39322         var vh = csize.height - (tbh + bbh);
39323
39324         s.setSize(vw, vh);
39325
39326         var bt = this.getBodyTable();
39327         
39328         if(cm.getLockedCount() == cm.config.length){
39329             bt = this.getLockedTable();
39330         }
39331         
39332         var ltWidth = hasLock ?
39333                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39334
39335         var scrollHeight = bt.offsetHeight;
39336         var scrollWidth = ltWidth + bt.offsetWidth;
39337         var vscroll = false, hscroll = false;
39338
39339         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39340
39341         var lw = this.lockedWrap, mw = this.mainWrap;
39342         var lb = this.lockedBody, mb = this.mainBody;
39343
39344         setTimeout(function(){
39345             var t = s.dom.offsetTop;
39346             var w = s.dom.clientWidth,
39347                 h = s.dom.clientHeight;
39348
39349             lw.setTop(t);
39350             lw.setSize(ltWidth, h);
39351
39352             mw.setLeftTop(ltWidth, t);
39353             mw.setSize(w-ltWidth, h);
39354
39355             lb.setHeight(h-hdHeight);
39356             mb.setHeight(h-hdHeight);
39357
39358             if(is2ndPass !== true && !gv.userResized && expandCol){
39359                 // high speed resize without full column calculation
39360                 
39361                 var ci = cm.getIndexById(expandCol);
39362                 if (ci < 0) {
39363                     ci = cm.findColumnIndex(expandCol);
39364                 }
39365                 ci = Math.max(0, ci); // make sure it's got at least the first col.
39366                 var expandId = cm.getColumnId(ci);
39367                 var  tw = cm.getTotalWidth(false);
39368                 var currentWidth = cm.getColumnWidth(ci);
39369                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39370                 if(currentWidth != cw){
39371                     cm.setColumnWidth(ci, cw, true);
39372                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39373                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39374                     gv.updateSplitters();
39375                     gv.layout(false, true);
39376                 }
39377             }
39378
39379             if(initialRender){
39380                 lw.show();
39381                 mw.show();
39382             }
39383             //c.endMeasure();
39384         }, 10);
39385     },
39386
39387     onWindowResize : function(){
39388         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39389             return;
39390         }
39391         this.layout();
39392     },
39393
39394     appendFooter : function(parentEl){
39395         return null;
39396     },
39397
39398     sortAscText : "Sort Ascending",
39399     sortDescText : "Sort Descending",
39400     lockText : "Lock Column",
39401     unlockText : "Unlock Column",
39402     columnsText : "Columns",
39403  
39404     columnsWiderText : "Wider",
39405     columnsNarrowText : "Thinner"
39406 });
39407
39408
39409 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39410     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39411     this.proxy.el.addClass('x-grid3-col-dd');
39412 };
39413
39414 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39415     handleMouseDown : function(e){
39416
39417     },
39418
39419     callHandleMouseDown : function(e){
39420         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39421     }
39422 });
39423 /*
39424  * Based on:
39425  * Ext JS Library 1.1.1
39426  * Copyright(c) 2006-2007, Ext JS, LLC.
39427  *
39428  * Originally Released Under LGPL - original licence link has changed is not relivant.
39429  *
39430  * Fork - LGPL
39431  * <script type="text/javascript">
39432  */
39433  /**
39434  * @extends Roo.dd.DDProxy
39435  * @class Roo.grid.SplitDragZone
39436  * Support for Column Header resizing
39437  * @constructor
39438  * @param {Object} config
39439  */
39440 // private
39441 // This is a support class used internally by the Grid components
39442 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39443     this.grid = grid;
39444     this.view = grid.getView();
39445     this.proxy = this.view.resizeProxy;
39446     Roo.grid.SplitDragZone.superclass.constructor.call(
39447         this,
39448         hd, // ID
39449         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39450         {  // CONFIG
39451             dragElId : Roo.id(this.proxy.dom),
39452             resizeFrame:false
39453         }
39454     );
39455     
39456     this.setHandleElId(Roo.id(hd));
39457     if (hd2 !== false) {
39458         this.setOuterHandleElId(Roo.id(hd2));
39459     }
39460     
39461     this.scroll = false;
39462 };
39463 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39464     fly: Roo.Element.fly,
39465
39466     b4StartDrag : function(x, y){
39467         this.view.headersDisabled = true;
39468         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39469                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39470         );
39471         this.proxy.setHeight(h);
39472         
39473         // for old system colWidth really stored the actual width?
39474         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39475         // which in reality did not work.. - it worked only for fixed sizes
39476         // for resizable we need to use actual sizes.
39477         var w = this.cm.getColumnWidth(this.cellIndex);
39478         if (!this.view.mainWrap) {
39479             // bootstrap.
39480             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39481         }
39482         
39483         
39484         
39485         // this was w-this.grid.minColumnWidth;
39486         // doesnt really make sense? - w = thie curren width or the rendered one?
39487         var minw = Math.max(w-this.grid.minColumnWidth, 0);
39488         this.resetConstraints();
39489         this.setXConstraint(minw, 1000);
39490         this.setYConstraint(0, 0);
39491         this.minX = x - minw;
39492         this.maxX = x + 1000;
39493         this.startPos = x;
39494         if (!this.view.mainWrap) { // this is Bootstrap code..
39495             this.getDragEl().style.display='block';
39496         }
39497         
39498         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39499     },
39500
39501
39502     handleMouseDown : function(e){
39503         ev = Roo.EventObject.setEvent(e);
39504         var t = this.fly(ev.getTarget());
39505         if(t.hasClass("x-grid-split")){
39506             this.cellIndex = this.view.getCellIndex(t.dom);
39507             this.split = t.dom;
39508             this.cm = this.grid.colModel;
39509             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39510                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39511             }
39512         }
39513     },
39514
39515     endDrag : function(e){
39516         this.view.headersDisabled = false;
39517         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39518         var diff = endX - this.startPos;
39519         // 
39520         var w = this.cm.getColumnWidth(this.cellIndex);
39521         if (!this.view.mainWrap) {
39522             w = 0;
39523         }
39524         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39525     },
39526
39527     autoOffset : function(){
39528         this.setDelta(0,0);
39529     }
39530 });/*
39531  * Based on:
39532  * Ext JS Library 1.1.1
39533  * Copyright(c) 2006-2007, Ext JS, LLC.
39534  *
39535  * Originally Released Under LGPL - original licence link has changed is not relivant.
39536  *
39537  * Fork - LGPL
39538  * <script type="text/javascript">
39539  */
39540  
39541 // private
39542 // This is a support class used internally by the Grid components
39543 Roo.grid.GridDragZone = function(grid, config){
39544     this.view = grid.getView();
39545     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39546     if(this.view.lockedBody){
39547         this.setHandleElId(Roo.id(this.view.mainBody.dom));
39548         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39549     }
39550     this.scroll = false;
39551     this.grid = grid;
39552     this.ddel = document.createElement('div');
39553     this.ddel.className = 'x-grid-dd-wrap';
39554 };
39555
39556 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39557     ddGroup : "GridDD",
39558
39559     getDragData : function(e){
39560         var t = Roo.lib.Event.getTarget(e);
39561         var rowIndex = this.view.findRowIndex(t);
39562         var sm = this.grid.selModel;
39563             
39564         //Roo.log(rowIndex);
39565         
39566         if (sm.getSelectedCell) {
39567             // cell selection..
39568             if (!sm.getSelectedCell()) {
39569                 return false;
39570             }
39571             if (rowIndex != sm.getSelectedCell()[0]) {
39572                 return false;
39573             }
39574         
39575         }
39576         if (sm.getSelections && sm.getSelections().length < 1) {
39577             return false;
39578         }
39579         
39580         
39581         // before it used to all dragging of unseleted... - now we dont do that.
39582         if(rowIndex !== false){
39583             
39584             // if editorgrid.. 
39585             
39586             
39587             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39588                
39589             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39590               //  
39591             //}
39592             if (e.hasModifier()){
39593                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39594             }
39595             
39596             Roo.log("getDragData");
39597             
39598             return {
39599                 grid: this.grid,
39600                 ddel: this.ddel,
39601                 rowIndex: rowIndex,
39602                 selections: sm.getSelections ? sm.getSelections() : (
39603                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39604             };
39605         }
39606         return false;
39607     },
39608     
39609     
39610     onInitDrag : function(e){
39611         var data = this.dragData;
39612         this.ddel.innerHTML = this.grid.getDragDropText();
39613         this.proxy.update(this.ddel);
39614         // fire start drag?
39615     },
39616
39617     afterRepair : function(){
39618         this.dragging = false;
39619     },
39620
39621     getRepairXY : function(e, data){
39622         return false;
39623     },
39624
39625     onEndDrag : function(data, e){
39626         // fire end drag?
39627     },
39628
39629     onValidDrop : function(dd, e, id){
39630         // fire drag drop?
39631         this.hideProxy();
39632     },
39633
39634     beforeInvalidDrop : function(e, id){
39635
39636     }
39637 });/*
39638  * Based on:
39639  * Ext JS Library 1.1.1
39640  * Copyright(c) 2006-2007, Ext JS, LLC.
39641  *
39642  * Originally Released Under LGPL - original licence link has changed is not relivant.
39643  *
39644  * Fork - LGPL
39645  * <script type="text/javascript">
39646  */
39647  
39648
39649 /**
39650  * @class Roo.grid.ColumnModel
39651  * @extends Roo.util.Observable
39652  * This is the default implementation of a ColumnModel used by the Grid. It defines
39653  * the columns in the grid.
39654  * <br>Usage:<br>
39655  <pre><code>
39656  var colModel = new Roo.grid.ColumnModel([
39657         {header: "Ticker", width: 60, sortable: true, locked: true},
39658         {header: "Company Name", width: 150, sortable: true},
39659         {header: "Market Cap.", width: 100, sortable: true},
39660         {header: "$ Sales", width: 100, sortable: true, renderer: money},
39661         {header: "Employees", width: 100, sortable: true, resizable: false}
39662  ]);
39663  </code></pre>
39664  * <p>
39665  
39666  * The config options listed for this class are options which may appear in each
39667  * individual column definition.
39668  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39669  * @constructor
39670  * @param {Object} config An Array of column config objects. See this class's
39671  * config objects for details.
39672 */
39673 Roo.grid.ColumnModel = function(config){
39674         /**
39675      * The config passed into the constructor
39676      */
39677     this.config = []; //config;
39678     this.lookup = {};
39679
39680     // if no id, create one
39681     // if the column does not have a dataIndex mapping,
39682     // map it to the order it is in the config
39683     for(var i = 0, len = config.length; i < len; i++){
39684         this.addColumn(config[i]);
39685         
39686     }
39687
39688     /**
39689      * The width of columns which have no width specified (defaults to 100)
39690      * @type Number
39691      */
39692     this.defaultWidth = 100;
39693
39694     /**
39695      * Default sortable of columns which have no sortable specified (defaults to false)
39696      * @type Boolean
39697      */
39698     this.defaultSortable = false;
39699
39700     this.addEvents({
39701         /**
39702              * @event widthchange
39703              * Fires when the width of a column changes.
39704              * @param {ColumnModel} this
39705              * @param {Number} columnIndex The column index
39706              * @param {Number} newWidth The new width
39707              */
39708             "widthchange": true,
39709         /**
39710              * @event headerchange
39711              * Fires when the text of a header changes.
39712              * @param {ColumnModel} this
39713              * @param {Number} columnIndex The column index
39714              * @param {Number} newText The new header text
39715              */
39716             "headerchange": true,
39717         /**
39718              * @event hiddenchange
39719              * Fires when a column is hidden or "unhidden".
39720              * @param {ColumnModel} this
39721              * @param {Number} columnIndex The column index
39722              * @param {Boolean} hidden true if hidden, false otherwise
39723              */
39724             "hiddenchange": true,
39725             /**
39726          * @event columnmoved
39727          * Fires when a column is moved.
39728          * @param {ColumnModel} this
39729          * @param {Number} oldIndex
39730          * @param {Number} newIndex
39731          */
39732         "columnmoved" : true,
39733         /**
39734          * @event columlockchange
39735          * Fires when a column's locked state is changed
39736          * @param {ColumnModel} this
39737          * @param {Number} colIndex
39738          * @param {Boolean} locked true if locked
39739          */
39740         "columnlockchange" : true
39741     });
39742     Roo.grid.ColumnModel.superclass.constructor.call(this);
39743 };
39744 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39745     /**
39746      * @cfg {String} header The header text to display in the Grid view.
39747      */
39748         /**
39749      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
39750      */
39751         /**
39752      * @cfg {String} smHeader Header at Bootsrap Small width
39753      */
39754         /**
39755      * @cfg {String} mdHeader Header at Bootsrap Medium width
39756      */
39757         /**
39758      * @cfg {String} lgHeader Header at Bootsrap Large width
39759      */
39760         /**
39761      * @cfg {String} xlHeader Header at Bootsrap extra Large width
39762      */
39763     /**
39764      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39765      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39766      * specified, the column's index is used as an index into the Record's data Array.
39767      */
39768     /**
39769      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39770      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39771      */
39772     /**
39773      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39774      * Defaults to the value of the {@link #defaultSortable} property.
39775      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39776      */
39777     /**
39778      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39779      */
39780     /**
39781      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39782      */
39783     /**
39784      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39785      */
39786     /**
39787      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39788      */
39789     /**
39790      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39791      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39792      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
39793      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39794      */
39795        /**
39796      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39797      */
39798     /**
39799      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39800      */
39801     /**
39802      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
39803      */
39804     /**
39805      * @cfg {String} cursor (Optional)
39806      */
39807     /**
39808      * @cfg {String} tooltip (Optional)
39809      */
39810     /**
39811      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
39812      */
39813     /**
39814      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
39815      */
39816     /**
39817      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
39818      */
39819     /**
39820      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
39821      */
39822         /**
39823      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
39824      */
39825     /**
39826      * Returns the id of the column at the specified index.
39827      * @param {Number} index The column index
39828      * @return {String} the id
39829      */
39830     getColumnId : function(index){
39831         return this.config[index].id;
39832     },
39833
39834     /**
39835      * Returns the column for a specified id.
39836      * @param {String} id The column id
39837      * @return {Object} the column
39838      */
39839     getColumnById : function(id){
39840         return this.lookup[id];
39841     },
39842
39843     
39844     /**
39845      * Returns the column Object for a specified dataIndex.
39846      * @param {String} dataIndex The column dataIndex
39847      * @return {Object|Boolean} the column or false if not found
39848      */
39849     getColumnByDataIndex: function(dataIndex){
39850         var index = this.findColumnIndex(dataIndex);
39851         return index > -1 ? this.config[index] : false;
39852     },
39853     
39854     /**
39855      * Returns the index for a specified column id.
39856      * @param {String} id The column id
39857      * @return {Number} the index, or -1 if not found
39858      */
39859     getIndexById : function(id){
39860         for(var i = 0, len = this.config.length; i < len; i++){
39861             if(this.config[i].id == id){
39862                 return i;
39863             }
39864         }
39865         return -1;
39866     },
39867     
39868     /**
39869      * Returns the index for a specified column dataIndex.
39870      * @param {String} dataIndex The column dataIndex
39871      * @return {Number} the index, or -1 if not found
39872      */
39873     
39874     findColumnIndex : function(dataIndex){
39875         for(var i = 0, len = this.config.length; i < len; i++){
39876             if(this.config[i].dataIndex == dataIndex){
39877                 return i;
39878             }
39879         }
39880         return -1;
39881     },
39882     
39883     
39884     moveColumn : function(oldIndex, newIndex){
39885         var c = this.config[oldIndex];
39886         this.config.splice(oldIndex, 1);
39887         this.config.splice(newIndex, 0, c);
39888         this.dataMap = null;
39889         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39890     },
39891
39892     isLocked : function(colIndex){
39893         return this.config[colIndex].locked === true;
39894     },
39895
39896     setLocked : function(colIndex, value, suppressEvent){
39897         if(this.isLocked(colIndex) == value){
39898             return;
39899         }
39900         this.config[colIndex].locked = value;
39901         if(!suppressEvent){
39902             this.fireEvent("columnlockchange", this, colIndex, value);
39903         }
39904     },
39905
39906     getTotalLockedWidth : function(){
39907         var totalWidth = 0;
39908         for(var i = 0; i < this.config.length; i++){
39909             if(this.isLocked(i) && !this.isHidden(i)){
39910                 this.totalWidth += this.getColumnWidth(i);
39911             }
39912         }
39913         return totalWidth;
39914     },
39915
39916     getLockedCount : function(){
39917         for(var i = 0, len = this.config.length; i < len; i++){
39918             if(!this.isLocked(i)){
39919                 return i;
39920             }
39921         }
39922         
39923         return this.config.length;
39924     },
39925
39926     /**
39927      * Returns the number of columns.
39928      * @return {Number}
39929      */
39930     getColumnCount : function(visibleOnly){
39931         if(visibleOnly === true){
39932             var c = 0;
39933             for(var i = 0, len = this.config.length; i < len; i++){
39934                 if(!this.isHidden(i)){
39935                     c++;
39936                 }
39937             }
39938             return c;
39939         }
39940         return this.config.length;
39941     },
39942
39943     /**
39944      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39945      * @param {Function} fn
39946      * @param {Object} scope (optional)
39947      * @return {Array} result
39948      */
39949     getColumnsBy : function(fn, scope){
39950         var r = [];
39951         for(var i = 0, len = this.config.length; i < len; i++){
39952             var c = this.config[i];
39953             if(fn.call(scope||this, c, i) === true){
39954                 r[r.length] = c;
39955             }
39956         }
39957         return r;
39958     },
39959
39960     /**
39961      * Returns true if the specified column is sortable.
39962      * @param {Number} col The column index
39963      * @return {Boolean}
39964      */
39965     isSortable : function(col){
39966         if(typeof this.config[col].sortable == "undefined"){
39967             return this.defaultSortable;
39968         }
39969         return this.config[col].sortable;
39970     },
39971
39972     /**
39973      * Returns the rendering (formatting) function defined for the column.
39974      * @param {Number} col The column index.
39975      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39976      */
39977     getRenderer : function(col){
39978         if(!this.config[col].renderer){
39979             return Roo.grid.ColumnModel.defaultRenderer;
39980         }
39981         return this.config[col].renderer;
39982     },
39983
39984     /**
39985      * Sets the rendering (formatting) function for a column.
39986      * @param {Number} col The column index
39987      * @param {Function} fn The function to use to process the cell's raw data
39988      * to return HTML markup for the grid view. The render function is called with
39989      * the following parameters:<ul>
39990      * <li>Data value.</li>
39991      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39992      * <li>css A CSS style string to apply to the table cell.</li>
39993      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39994      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39995      * <li>Row index</li>
39996      * <li>Column index</li>
39997      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39998      */
39999     setRenderer : function(col, fn){
40000         this.config[col].renderer = fn;
40001     },
40002
40003     /**
40004      * Returns the width for the specified column.
40005      * @param {Number} col The column index
40006      * @param (optional) {String} gridSize bootstrap width size.
40007      * @return {Number}
40008      */
40009     getColumnWidth : function(col, gridSize)
40010         {
40011                 var cfg = this.config[col];
40012                 
40013                 if (typeof(gridSize) == 'undefined') {
40014                         return cfg.width * 1 || this.defaultWidth;
40015                 }
40016                 if (gridSize === false) { // if we set it..
40017                         return cfg.width || false;
40018                 }
40019                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40020                 
40021                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40022                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40023                                 continue;
40024                         }
40025                         return cfg[ sizes[i] ];
40026                 }
40027                 return 1;
40028                 
40029     },
40030
40031     /**
40032      * Sets the width for a column.
40033      * @param {Number} col The column index
40034      * @param {Number} width The new width
40035      */
40036     setColumnWidth : function(col, width, suppressEvent){
40037         this.config[col].width = width;
40038         this.totalWidth = null;
40039         if(!suppressEvent){
40040              this.fireEvent("widthchange", this, col, width);
40041         }
40042     },
40043
40044     /**
40045      * Returns the total width of all columns.
40046      * @param {Boolean} includeHidden True to include hidden column widths
40047      * @return {Number}
40048      */
40049     getTotalWidth : function(includeHidden){
40050         if(!this.totalWidth){
40051             this.totalWidth = 0;
40052             for(var i = 0, len = this.config.length; i < len; i++){
40053                 if(includeHidden || !this.isHidden(i)){
40054                     this.totalWidth += this.getColumnWidth(i);
40055                 }
40056             }
40057         }
40058         return this.totalWidth;
40059     },
40060
40061     /**
40062      * Returns the header for the specified column.
40063      * @param {Number} col The column index
40064      * @return {String}
40065      */
40066     getColumnHeader : function(col){
40067         return this.config[col].header;
40068     },
40069
40070     /**
40071      * Sets the header for a column.
40072      * @param {Number} col The column index
40073      * @param {String} header The new header
40074      */
40075     setColumnHeader : function(col, header){
40076         this.config[col].header = header;
40077         this.fireEvent("headerchange", this, col, header);
40078     },
40079
40080     /**
40081      * Returns the tooltip for the specified column.
40082      * @param {Number} col The column index
40083      * @return {String}
40084      */
40085     getColumnTooltip : function(col){
40086             return this.config[col].tooltip;
40087     },
40088     /**
40089      * Sets the tooltip for a column.
40090      * @param {Number} col The column index
40091      * @param {String} tooltip The new tooltip
40092      */
40093     setColumnTooltip : function(col, tooltip){
40094             this.config[col].tooltip = tooltip;
40095     },
40096
40097     /**
40098      * Returns the dataIndex for the specified column.
40099      * @param {Number} col The column index
40100      * @return {Number}
40101      */
40102     getDataIndex : function(col){
40103         return this.config[col].dataIndex;
40104     },
40105
40106     /**
40107      * Sets the dataIndex for a column.
40108      * @param {Number} col The column index
40109      * @param {Number} dataIndex The new dataIndex
40110      */
40111     setDataIndex : function(col, dataIndex){
40112         this.config[col].dataIndex = dataIndex;
40113     },
40114
40115     
40116     
40117     /**
40118      * Returns true if the cell is editable.
40119      * @param {Number} colIndex The column index
40120      * @param {Number} rowIndex The row index - this is nto actually used..?
40121      * @return {Boolean}
40122      */
40123     isCellEditable : function(colIndex, rowIndex){
40124         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40125     },
40126
40127     /**
40128      * Returns the editor defined for the cell/column.
40129      * return false or null to disable editing.
40130      * @param {Number} colIndex The column index
40131      * @param {Number} rowIndex The row index
40132      * @return {Object}
40133      */
40134     getCellEditor : function(colIndex, rowIndex){
40135         return this.config[colIndex].editor;
40136     },
40137
40138     /**
40139      * Sets if a column is editable.
40140      * @param {Number} col The column index
40141      * @param {Boolean} editable True if the column is editable
40142      */
40143     setEditable : function(col, editable){
40144         this.config[col].editable = editable;
40145     },
40146
40147
40148     /**
40149      * Returns true if the column is hidden.
40150      * @param {Number} colIndex The column index
40151      * @return {Boolean}
40152      */
40153     isHidden : function(colIndex){
40154         return this.config[colIndex].hidden;
40155     },
40156
40157
40158     /**
40159      * Returns true if the column width cannot be changed
40160      */
40161     isFixed : function(colIndex){
40162         return this.config[colIndex].fixed;
40163     },
40164
40165     /**
40166      * Returns true if the column can be resized
40167      * @return {Boolean}
40168      */
40169     isResizable : function(colIndex){
40170         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40171     },
40172     /**
40173      * Sets if a column is hidden.
40174      * @param {Number} colIndex The column index
40175      * @param {Boolean} hidden True if the column is hidden
40176      */
40177     setHidden : function(colIndex, hidden){
40178         this.config[colIndex].hidden = hidden;
40179         this.totalWidth = null;
40180         this.fireEvent("hiddenchange", this, colIndex, hidden);
40181     },
40182
40183     /**
40184      * Sets the editor for a column.
40185      * @param {Number} col The column index
40186      * @param {Object} editor The editor object
40187      */
40188     setEditor : function(col, editor){
40189         this.config[col].editor = editor;
40190     },
40191     /**
40192      * Add a column (experimental...) - defaults to adding to the end..
40193      * @param {Object} config 
40194     */
40195     addColumn : function(c)
40196     {
40197     
40198         var i = this.config.length;
40199         this.config[i] = c;
40200         
40201         if(typeof c.dataIndex == "undefined"){
40202             c.dataIndex = i;
40203         }
40204         if(typeof c.renderer == "string"){
40205             c.renderer = Roo.util.Format[c.renderer];
40206         }
40207         if(typeof c.id == "undefined"){
40208             c.id = Roo.id();
40209         }
40210         if(c.editor && c.editor.xtype){
40211             c.editor  = Roo.factory(c.editor, Roo.grid);
40212         }
40213         if(c.editor && c.editor.isFormField){
40214             c.editor = new Roo.grid.GridEditor(c.editor);
40215         }
40216         this.lookup[c.id] = c;
40217     }
40218     
40219 });
40220
40221 Roo.grid.ColumnModel.defaultRenderer = function(value)
40222 {
40223     if(typeof value == "object") {
40224         return value;
40225     }
40226         if(typeof value == "string" && value.length < 1){
40227             return "&#160;";
40228         }
40229     
40230         return String.format("{0}", value);
40231 };
40232
40233 // Alias for backwards compatibility
40234 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40235 /*
40236  * Based on:
40237  * Ext JS Library 1.1.1
40238  * Copyright(c) 2006-2007, Ext JS, LLC.
40239  *
40240  * Originally Released Under LGPL - original licence link has changed is not relivant.
40241  *
40242  * Fork - LGPL
40243  * <script type="text/javascript">
40244  */
40245
40246 /**
40247  * @class Roo.grid.AbstractSelectionModel
40248  * @extends Roo.util.Observable
40249  * @abstract
40250  * Abstract base class for grid SelectionModels.  It provides the interface that should be
40251  * implemented by descendant classes.  This class should not be directly instantiated.
40252  * @constructor
40253  */
40254 Roo.grid.AbstractSelectionModel = function(){
40255     this.locked = false;
40256     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40257 };
40258
40259 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
40260     /** @ignore Called by the grid automatically. Do not call directly. */
40261     init : function(grid){
40262         this.grid = grid;
40263         this.initEvents();
40264     },
40265
40266     /**
40267      * Locks the selections.
40268      */
40269     lock : function(){
40270         this.locked = true;
40271     },
40272
40273     /**
40274      * Unlocks the selections.
40275      */
40276     unlock : function(){
40277         this.locked = false;
40278     },
40279
40280     /**
40281      * Returns true if the selections are locked.
40282      * @return {Boolean}
40283      */
40284     isLocked : function(){
40285         return this.locked;
40286     }
40287 });/*
40288  * Based on:
40289  * Ext JS Library 1.1.1
40290  * Copyright(c) 2006-2007, Ext JS, LLC.
40291  *
40292  * Originally Released Under LGPL - original licence link has changed is not relivant.
40293  *
40294  * Fork - LGPL
40295  * <script type="text/javascript">
40296  */
40297 /**
40298  * @extends Roo.grid.AbstractSelectionModel
40299  * @class Roo.grid.RowSelectionModel
40300  * The default SelectionModel used by {@link Roo.grid.Grid}.
40301  * It supports multiple selections and keyboard selection/navigation. 
40302  * @constructor
40303  * @param {Object} config
40304  */
40305 Roo.grid.RowSelectionModel = function(config){
40306     Roo.apply(this, config);
40307     this.selections = new Roo.util.MixedCollection(false, function(o){
40308         return o.id;
40309     });
40310
40311     this.last = false;
40312     this.lastActive = false;
40313
40314     this.addEvents({
40315         /**
40316         * @event selectionchange
40317         * Fires when the selection changes
40318         * @param {SelectionModel} this
40319         */
40320        "selectionchange" : true,
40321        /**
40322         * @event afterselectionchange
40323         * Fires after the selection changes (eg. by key press or clicking)
40324         * @param {SelectionModel} this
40325         */
40326        "afterselectionchange" : true,
40327        /**
40328         * @event beforerowselect
40329         * Fires when a row is selected being selected, return false to cancel.
40330         * @param {SelectionModel} this
40331         * @param {Number} rowIndex The selected index
40332         * @param {Boolean} keepExisting False if other selections will be cleared
40333         */
40334        "beforerowselect" : true,
40335        /**
40336         * @event rowselect
40337         * Fires when a row is selected.
40338         * @param {SelectionModel} this
40339         * @param {Number} rowIndex The selected index
40340         * @param {Roo.data.Record} r The record
40341         */
40342        "rowselect" : true,
40343        /**
40344         * @event rowdeselect
40345         * Fires when a row is deselected.
40346         * @param {SelectionModel} this
40347         * @param {Number} rowIndex The selected index
40348         */
40349         "rowdeselect" : true
40350     });
40351     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40352     this.locked = false;
40353 };
40354
40355 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
40356     /**
40357      * @cfg {Boolean} singleSelect
40358      * True to allow selection of only one row at a time (defaults to false)
40359      */
40360     singleSelect : false,
40361
40362     // private
40363     initEvents : function(){
40364
40365         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40366             this.grid.on("mousedown", this.handleMouseDown, this);
40367         }else{ // allow click to work like normal
40368             this.grid.on("rowclick", this.handleDragableRowClick, this);
40369         }
40370         // bootstrap does not have a view..
40371         var view = this.grid.view ? this.grid.view : this.grid;
40372         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40373             "up" : function(e){
40374                 if(!e.shiftKey){
40375                     this.selectPrevious(e.shiftKey);
40376                 }else if(this.last !== false && this.lastActive !== false){
40377                     var last = this.last;
40378                     this.selectRange(this.last,  this.lastActive-1);
40379                     view.focusRow(this.lastActive);
40380                     if(last !== false){
40381                         this.last = last;
40382                     }
40383                 }else{
40384                     this.selectFirstRow();
40385                 }
40386                 this.fireEvent("afterselectionchange", this);
40387             },
40388             "down" : function(e){
40389                 if(!e.shiftKey){
40390                     this.selectNext(e.shiftKey);
40391                 }else if(this.last !== false && this.lastActive !== false){
40392                     var last = this.last;
40393                     this.selectRange(this.last,  this.lastActive+1);
40394                     view.focusRow(this.lastActive);
40395                     if(last !== false){
40396                         this.last = last;
40397                     }
40398                 }else{
40399                     this.selectFirstRow();
40400                 }
40401                 this.fireEvent("afterselectionchange", this);
40402             },
40403             scope: this
40404         });
40405
40406          
40407         view.on("refresh", this.onRefresh, this);
40408         view.on("rowupdated", this.onRowUpdated, this);
40409         view.on("rowremoved", this.onRemove, this);
40410     },
40411
40412     // private
40413     onRefresh : function(){
40414         var ds = this.grid.ds, i, v = this.grid.view;
40415         var s = this.selections;
40416         s.each(function(r){
40417             if((i = ds.indexOfId(r.id)) != -1){
40418                 v.onRowSelect(i);
40419                 s.add(ds.getAt(i)); // updating the selection relate data
40420             }else{
40421                 s.remove(r);
40422             }
40423         });
40424     },
40425
40426     // private
40427     onRemove : function(v, index, r){
40428         this.selections.remove(r);
40429     },
40430
40431     // private
40432     onRowUpdated : function(v, index, r){
40433         if(this.isSelected(r)){
40434             v.onRowSelect(index);
40435         }
40436     },
40437
40438     /**
40439      * Select records.
40440      * @param {Array} records The records to select
40441      * @param {Boolean} keepExisting (optional) True to keep existing selections
40442      */
40443     selectRecords : function(records, keepExisting){
40444         if(!keepExisting){
40445             this.clearSelections();
40446         }
40447         var ds = this.grid.ds;
40448         for(var i = 0, len = records.length; i < len; i++){
40449             this.selectRow(ds.indexOf(records[i]), true);
40450         }
40451     },
40452
40453     /**
40454      * Gets the number of selected rows.
40455      * @return {Number}
40456      */
40457     getCount : function(){
40458         return this.selections.length;
40459     },
40460
40461     /**
40462      * Selects the first row in the grid.
40463      */
40464     selectFirstRow : function(){
40465         this.selectRow(0);
40466     },
40467
40468     /**
40469      * Select the last row.
40470      * @param {Boolean} keepExisting (optional) True to keep existing selections
40471      */
40472     selectLastRow : function(keepExisting){
40473         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40474     },
40475
40476     /**
40477      * Selects the row immediately following the last selected row.
40478      * @param {Boolean} keepExisting (optional) True to keep existing selections
40479      */
40480     selectNext : function(keepExisting){
40481         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40482             this.selectRow(this.last+1, keepExisting);
40483             var view = this.grid.view ? this.grid.view : this.grid;
40484             view.focusRow(this.last);
40485         }
40486     },
40487
40488     /**
40489      * Selects the row that precedes the last selected row.
40490      * @param {Boolean} keepExisting (optional) True to keep existing selections
40491      */
40492     selectPrevious : function(keepExisting){
40493         if(this.last){
40494             this.selectRow(this.last-1, keepExisting);
40495             var view = this.grid.view ? this.grid.view : this.grid;
40496             view.focusRow(this.last);
40497         }
40498     },
40499
40500     /**
40501      * Returns the selected records
40502      * @return {Array} Array of selected records
40503      */
40504     getSelections : function(){
40505         return [].concat(this.selections.items);
40506     },
40507
40508     /**
40509      * Returns the first selected record.
40510      * @return {Record}
40511      */
40512     getSelected : function(){
40513         return this.selections.itemAt(0);
40514     },
40515
40516
40517     /**
40518      * Clears all selections.
40519      */
40520     clearSelections : function(fast){
40521         if(this.locked) {
40522             return;
40523         }
40524         if(fast !== true){
40525             var ds = this.grid.ds;
40526             var s = this.selections;
40527             s.each(function(r){
40528                 this.deselectRow(ds.indexOfId(r.id));
40529             }, this);
40530             s.clear();
40531         }else{
40532             this.selections.clear();
40533         }
40534         this.last = false;
40535     },
40536
40537
40538     /**
40539      * Selects all rows.
40540      */
40541     selectAll : function(){
40542         if(this.locked) {
40543             return;
40544         }
40545         this.selections.clear();
40546         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40547             this.selectRow(i, true);
40548         }
40549     },
40550
40551     /**
40552      * Returns True if there is a selection.
40553      * @return {Boolean}
40554      */
40555     hasSelection : function(){
40556         return this.selections.length > 0;
40557     },
40558
40559     /**
40560      * Returns True if the specified row is selected.
40561      * @param {Number/Record} record The record or index of the record to check
40562      * @return {Boolean}
40563      */
40564     isSelected : function(index){
40565         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40566         return (r && this.selections.key(r.id) ? true : false);
40567     },
40568
40569     /**
40570      * Returns True if the specified record id is selected.
40571      * @param {String} id The id of record to check
40572      * @return {Boolean}
40573      */
40574     isIdSelected : function(id){
40575         return (this.selections.key(id) ? true : false);
40576     },
40577
40578     // private
40579     handleMouseDown : function(e, t)
40580     {
40581         var view = this.grid.view ? this.grid.view : this.grid;
40582         var rowIndex;
40583         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40584             return;
40585         };
40586         if(e.shiftKey && this.last !== false){
40587             var last = this.last;
40588             this.selectRange(last, rowIndex, e.ctrlKey);
40589             this.last = last; // reset the last
40590             view.focusRow(rowIndex);
40591         }else{
40592             var isSelected = this.isSelected(rowIndex);
40593             if(e.button !== 0 && isSelected){
40594                 view.focusRow(rowIndex);
40595             }else if(e.ctrlKey && isSelected){
40596                 this.deselectRow(rowIndex);
40597             }else if(!isSelected){
40598                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40599                 view.focusRow(rowIndex);
40600             }
40601         }
40602         this.fireEvent("afterselectionchange", this);
40603     },
40604     // private
40605     handleDragableRowClick :  function(grid, rowIndex, e) 
40606     {
40607         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40608             this.selectRow(rowIndex, false);
40609             var view = this.grid.view ? this.grid.view : this.grid;
40610             view.focusRow(rowIndex);
40611              this.fireEvent("afterselectionchange", this);
40612         }
40613     },
40614     
40615     /**
40616      * Selects multiple rows.
40617      * @param {Array} rows Array of the indexes of the row to select
40618      * @param {Boolean} keepExisting (optional) True to keep existing selections
40619      */
40620     selectRows : function(rows, keepExisting){
40621         if(!keepExisting){
40622             this.clearSelections();
40623         }
40624         for(var i = 0, len = rows.length; i < len; i++){
40625             this.selectRow(rows[i], true);
40626         }
40627     },
40628
40629     /**
40630      * Selects a range of rows. All rows in between startRow and endRow are also selected.
40631      * @param {Number} startRow The index of the first row in the range
40632      * @param {Number} endRow The index of the last row in the range
40633      * @param {Boolean} keepExisting (optional) True to retain existing selections
40634      */
40635     selectRange : function(startRow, endRow, keepExisting){
40636         if(this.locked) {
40637             return;
40638         }
40639         if(!keepExisting){
40640             this.clearSelections();
40641         }
40642         if(startRow <= endRow){
40643             for(var i = startRow; i <= endRow; i++){
40644                 this.selectRow(i, true);
40645             }
40646         }else{
40647             for(var i = startRow; i >= endRow; i--){
40648                 this.selectRow(i, true);
40649             }
40650         }
40651     },
40652
40653     /**
40654      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40655      * @param {Number} startRow The index of the first row in the range
40656      * @param {Number} endRow The index of the last row in the range
40657      */
40658     deselectRange : function(startRow, endRow, preventViewNotify){
40659         if(this.locked) {
40660             return;
40661         }
40662         for(var i = startRow; i <= endRow; i++){
40663             this.deselectRow(i, preventViewNotify);
40664         }
40665     },
40666
40667     /**
40668      * Selects a row.
40669      * @param {Number} row The index of the row to select
40670      * @param {Boolean} keepExisting (optional) True to keep existing selections
40671      */
40672     selectRow : function(index, keepExisting, preventViewNotify){
40673         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40674             return;
40675         }
40676         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40677             if(!keepExisting || this.singleSelect){
40678                 this.clearSelections();
40679             }
40680             var r = this.grid.ds.getAt(index);
40681             this.selections.add(r);
40682             this.last = this.lastActive = index;
40683             if(!preventViewNotify){
40684                 var view = this.grid.view ? this.grid.view : this.grid;
40685                 view.onRowSelect(index);
40686             }
40687             this.fireEvent("rowselect", this, index, r);
40688             this.fireEvent("selectionchange", this);
40689         }
40690     },
40691
40692     /**
40693      * Deselects a row.
40694      * @param {Number} row The index of the row to deselect
40695      */
40696     deselectRow : function(index, preventViewNotify){
40697         if(this.locked) {
40698             return;
40699         }
40700         if(this.last == index){
40701             this.last = false;
40702         }
40703         if(this.lastActive == index){
40704             this.lastActive = false;
40705         }
40706         var r = this.grid.ds.getAt(index);
40707         this.selections.remove(r);
40708         if(!preventViewNotify){
40709             var view = this.grid.view ? this.grid.view : this.grid;
40710             view.onRowDeselect(index);
40711         }
40712         this.fireEvent("rowdeselect", this, index);
40713         this.fireEvent("selectionchange", this);
40714     },
40715
40716     // private
40717     restoreLast : function(){
40718         if(this._last){
40719             this.last = this._last;
40720         }
40721     },
40722
40723     // private
40724     acceptsNav : function(row, col, cm){
40725         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40726     },
40727
40728     // private
40729     onEditorKey : function(field, e){
40730         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
40731         if(k == e.TAB){
40732             e.stopEvent();
40733             ed.completeEdit();
40734             if(e.shiftKey){
40735                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40736             }else{
40737                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40738             }
40739         }else if(k == e.ENTER && !e.ctrlKey){
40740             e.stopEvent();
40741             ed.completeEdit();
40742             if(e.shiftKey){
40743                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40744             }else{
40745                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40746             }
40747         }else if(k == e.ESC){
40748             ed.cancelEdit();
40749         }
40750         if(newCell){
40751             g.startEditing(newCell[0], newCell[1]);
40752         }
40753     }
40754 });/*
40755  * Based on:
40756  * Ext JS Library 1.1.1
40757  * Copyright(c) 2006-2007, Ext JS, LLC.
40758  *
40759  * Originally Released Under LGPL - original licence link has changed is not relivant.
40760  *
40761  * Fork - LGPL
40762  * <script type="text/javascript">
40763  */
40764 /**
40765  * @class Roo.grid.CellSelectionModel
40766  * @extends Roo.grid.AbstractSelectionModel
40767  * This class provides the basic implementation for cell selection in a grid.
40768  * @constructor
40769  * @param {Object} config The object containing the configuration of this model.
40770  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40771  */
40772 Roo.grid.CellSelectionModel = function(config){
40773     Roo.apply(this, config);
40774
40775     this.selection = null;
40776
40777     this.addEvents({
40778         /**
40779              * @event beforerowselect
40780              * Fires before a cell is selected.
40781              * @param {SelectionModel} this
40782              * @param {Number} rowIndex The selected row index
40783              * @param {Number} colIndex The selected cell index
40784              */
40785             "beforecellselect" : true,
40786         /**
40787              * @event cellselect
40788              * Fires when a cell is selected.
40789              * @param {SelectionModel} this
40790              * @param {Number} rowIndex The selected row index
40791              * @param {Number} colIndex The selected cell index
40792              */
40793             "cellselect" : true,
40794         /**
40795              * @event selectionchange
40796              * Fires when the active selection changes.
40797              * @param {SelectionModel} this
40798              * @param {Object} selection null for no selection or an object (o) with two properties
40799                 <ul>
40800                 <li>o.record: the record object for the row the selection is in</li>
40801                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40802                 </ul>
40803              */
40804             "selectionchange" : true,
40805         /**
40806              * @event tabend
40807              * Fires when the tab (or enter) was pressed on the last editable cell
40808              * You can use this to trigger add new row.
40809              * @param {SelectionModel} this
40810              */
40811             "tabend" : true,
40812          /**
40813              * @event beforeeditnext
40814              * Fires before the next editable sell is made active
40815              * You can use this to skip to another cell or fire the tabend
40816              *    if you set cell to false
40817              * @param {Object} eventdata object : { cell : [ row, col ] } 
40818              */
40819             "beforeeditnext" : true
40820     });
40821     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40822 };
40823
40824 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40825     
40826     enter_is_tab: false,
40827
40828     /** @ignore */
40829     initEvents : function(){
40830         this.grid.on("mousedown", this.handleMouseDown, this);
40831         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40832         var view = this.grid.view;
40833         view.on("refresh", this.onViewChange, this);
40834         view.on("rowupdated", this.onRowUpdated, this);
40835         view.on("beforerowremoved", this.clearSelections, this);
40836         view.on("beforerowsinserted", this.clearSelections, this);
40837         if(this.grid.isEditor){
40838             this.grid.on("beforeedit", this.beforeEdit,  this);
40839         }
40840     },
40841
40842         //private
40843     beforeEdit : function(e){
40844         this.select(e.row, e.column, false, true, e.record);
40845     },
40846
40847         //private
40848     onRowUpdated : function(v, index, r){
40849         if(this.selection && this.selection.record == r){
40850             v.onCellSelect(index, this.selection.cell[1]);
40851         }
40852     },
40853
40854         //private
40855     onViewChange : function(){
40856         this.clearSelections(true);
40857     },
40858
40859         /**
40860          * Returns the currently selected cell,.
40861          * @return {Array} The selected cell (row, column) or null if none selected.
40862          */
40863     getSelectedCell : function(){
40864         return this.selection ? this.selection.cell : null;
40865     },
40866
40867     /**
40868      * Clears all selections.
40869      * @param {Boolean} true to prevent the gridview from being notified about the change.
40870      */
40871     clearSelections : function(preventNotify){
40872         var s = this.selection;
40873         if(s){
40874             if(preventNotify !== true){
40875                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40876             }
40877             this.selection = null;
40878             this.fireEvent("selectionchange", this, null);
40879         }
40880     },
40881
40882     /**
40883      * Returns true if there is a selection.
40884      * @return {Boolean}
40885      */
40886     hasSelection : function(){
40887         return this.selection ? true : false;
40888     },
40889
40890     /** @ignore */
40891     handleMouseDown : function(e, t){
40892         var v = this.grid.getView();
40893         if(this.isLocked()){
40894             return;
40895         };
40896         var row = v.findRowIndex(t);
40897         var cell = v.findCellIndex(t);
40898         if(row !== false && cell !== false){
40899             this.select(row, cell);
40900         }
40901     },
40902
40903     /**
40904      * Selects a cell.
40905      * @param {Number} rowIndex
40906      * @param {Number} collIndex
40907      */
40908     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40909         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40910             this.clearSelections();
40911             r = r || this.grid.dataSource.getAt(rowIndex);
40912             this.selection = {
40913                 record : r,
40914                 cell : [rowIndex, colIndex]
40915             };
40916             if(!preventViewNotify){
40917                 var v = this.grid.getView();
40918                 v.onCellSelect(rowIndex, colIndex);
40919                 if(preventFocus !== true){
40920                     v.focusCell(rowIndex, colIndex);
40921                 }
40922             }
40923             this.fireEvent("cellselect", this, rowIndex, colIndex);
40924             this.fireEvent("selectionchange", this, this.selection);
40925         }
40926     },
40927
40928         //private
40929     isSelectable : function(rowIndex, colIndex, cm){
40930         return !cm.isHidden(colIndex);
40931     },
40932
40933     /** @ignore */
40934     handleKeyDown : function(e){
40935         //Roo.log('Cell Sel Model handleKeyDown');
40936         if(!e.isNavKeyPress()){
40937             return;
40938         }
40939         var g = this.grid, s = this.selection;
40940         if(!s){
40941             e.stopEvent();
40942             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40943             if(cell){
40944                 this.select(cell[0], cell[1]);
40945             }
40946             return;
40947         }
40948         var sm = this;
40949         var walk = function(row, col, step){
40950             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40951         };
40952         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40953         var newCell;
40954
40955       
40956
40957         switch(k){
40958             case e.TAB:
40959                 // handled by onEditorKey
40960                 if (g.isEditor && g.editing) {
40961                     return;
40962                 }
40963                 if(e.shiftKey) {
40964                     newCell = walk(r, c-1, -1);
40965                 } else {
40966                     newCell = walk(r, c+1, 1);
40967                 }
40968                 break;
40969             
40970             case e.DOWN:
40971                newCell = walk(r+1, c, 1);
40972                 break;
40973             
40974             case e.UP:
40975                 newCell = walk(r-1, c, -1);
40976                 break;
40977             
40978             case e.RIGHT:
40979                 newCell = walk(r, c+1, 1);
40980                 break;
40981             
40982             case e.LEFT:
40983                 newCell = walk(r, c-1, -1);
40984                 break;
40985             
40986             case e.ENTER:
40987                 
40988                 if(g.isEditor && !g.editing){
40989                    g.startEditing(r, c);
40990                    e.stopEvent();
40991                    return;
40992                 }
40993                 
40994                 
40995              break;
40996         };
40997         if(newCell){
40998             this.select(newCell[0], newCell[1]);
40999             e.stopEvent();
41000             
41001         }
41002     },
41003
41004     acceptsNav : function(row, col, cm){
41005         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41006     },
41007     /**
41008      * Selects a cell.
41009      * @param {Number} field (not used) - as it's normally used as a listener
41010      * @param {Number} e - event - fake it by using
41011      *
41012      * var e = Roo.EventObjectImpl.prototype;
41013      * e.keyCode = e.TAB
41014      *
41015      * 
41016      */
41017     onEditorKey : function(field, e){
41018         
41019         var k = e.getKey(),
41020             newCell,
41021             g = this.grid,
41022             ed = g.activeEditor,
41023             forward = false;
41024         ///Roo.log('onEditorKey' + k);
41025         
41026         
41027         if (this.enter_is_tab && k == e.ENTER) {
41028             k = e.TAB;
41029         }
41030         
41031         if(k == e.TAB){
41032             if(e.shiftKey){
41033                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41034             }else{
41035                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41036                 forward = true;
41037             }
41038             
41039             e.stopEvent();
41040             
41041         } else if(k == e.ENTER &&  !e.ctrlKey){
41042             ed.completeEdit();
41043             e.stopEvent();
41044             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41045         
41046                 } else if(k == e.ESC){
41047             ed.cancelEdit();
41048         }
41049                 
41050         if (newCell) {
41051             var ecall = { cell : newCell, forward : forward };
41052             this.fireEvent('beforeeditnext', ecall );
41053             newCell = ecall.cell;
41054                         forward = ecall.forward;
41055         }
41056                 
41057         if(newCell){
41058             //Roo.log('next cell after edit');
41059             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41060         } else if (forward) {
41061             // tabbed past last
41062             this.fireEvent.defer(100, this, ['tabend',this]);
41063         }
41064     }
41065 });/*
41066  * Based on:
41067  * Ext JS Library 1.1.1
41068  * Copyright(c) 2006-2007, Ext JS, LLC.
41069  *
41070  * Originally Released Under LGPL - original licence link has changed is not relivant.
41071  *
41072  * Fork - LGPL
41073  * <script type="text/javascript">
41074  */
41075  
41076 /**
41077  * @class Roo.grid.EditorGrid
41078  * @extends Roo.grid.Grid
41079  * Class for creating and editable grid.
41080  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
41081  * The container MUST have some type of size defined for the grid to fill. The container will be 
41082  * automatically set to position relative if it isn't already.
41083  * @param {Object} dataSource The data model to bind to
41084  * @param {Object} colModel The column model with info about this grid's columns
41085  */
41086 Roo.grid.EditorGrid = function(container, config){
41087     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41088     this.getGridEl().addClass("xedit-grid");
41089
41090     if(!this.selModel){
41091         this.selModel = new Roo.grid.CellSelectionModel();
41092     }
41093
41094     this.activeEditor = null;
41095
41096         this.addEvents({
41097             /**
41098              * @event beforeedit
41099              * Fires before cell editing is triggered. The edit event object has the following properties <br />
41100              * <ul style="padding:5px;padding-left:16px;">
41101              * <li>grid - This grid</li>
41102              * <li>record - The record being edited</li>
41103              * <li>field - The field name being edited</li>
41104              * <li>value - The value for the field being edited.</li>
41105              * <li>row - The grid row index</li>
41106              * <li>column - The grid column index</li>
41107              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41108              * </ul>
41109              * @param {Object} e An edit event (see above for description)
41110              */
41111             "beforeedit" : true,
41112             /**
41113              * @event afteredit
41114              * Fires after a cell is edited. <br />
41115              * <ul style="padding:5px;padding-left:16px;">
41116              * <li>grid - This grid</li>
41117              * <li>record - The record being edited</li>
41118              * <li>field - The field name being edited</li>
41119              * <li>value - The value being set</li>
41120              * <li>originalValue - The original value for the field, before the edit.</li>
41121              * <li>row - The grid row index</li>
41122              * <li>column - The grid column index</li>
41123              * </ul>
41124              * @param {Object} e An edit event (see above for description)
41125              */
41126             "afteredit" : true,
41127             /**
41128              * @event validateedit
41129              * Fires after a cell is edited, but before the value is set in the record. 
41130          * You can use this to modify the value being set in the field, Return false
41131              * to cancel the change. The edit event object has the following properties <br />
41132              * <ul style="padding:5px;padding-left:16px;">
41133          * <li>editor - This editor</li>
41134              * <li>grid - This grid</li>
41135              * <li>record - The record being edited</li>
41136              * <li>field - The field name being edited</li>
41137              * <li>value - The value being set</li>
41138              * <li>originalValue - The original value for the field, before the edit.</li>
41139              * <li>row - The grid row index</li>
41140              * <li>column - The grid column index</li>
41141              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41142              * </ul>
41143              * @param {Object} e An edit event (see above for description)
41144              */
41145             "validateedit" : true
41146         });
41147     this.on("bodyscroll", this.stopEditing,  this);
41148     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
41149 };
41150
41151 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41152     /**
41153      * @cfg {Number} clicksToEdit
41154      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41155      */
41156     clicksToEdit: 2,
41157
41158     // private
41159     isEditor : true,
41160     // private
41161     trackMouseOver: false, // causes very odd FF errors
41162
41163     onCellDblClick : function(g, row, col){
41164         this.startEditing(row, col);
41165     },
41166
41167     onEditComplete : function(ed, value, startValue){
41168         this.editing = false;
41169         this.activeEditor = null;
41170         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41171         var r = ed.record;
41172         var field = this.colModel.getDataIndex(ed.col);
41173         var e = {
41174             grid: this,
41175             record: r,
41176             field: field,
41177             originalValue: startValue,
41178             value: value,
41179             row: ed.row,
41180             column: ed.col,
41181             cancel:false,
41182             editor: ed
41183         };
41184         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41185         cell.show();
41186           
41187         if(String(value) !== String(startValue)){
41188             
41189             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41190                 r.set(field, e.value);
41191                 // if we are dealing with a combo box..
41192                 // then we also set the 'name' colum to be the displayField
41193                 if (ed.field.displayField && ed.field.name) {
41194                     r.set(ed.field.name, ed.field.el.dom.value);
41195                 }
41196                 
41197                 delete e.cancel; //?? why!!!
41198                 this.fireEvent("afteredit", e);
41199             }
41200         } else {
41201             this.fireEvent("afteredit", e); // always fire it!
41202         }
41203         this.view.focusCell(ed.row, ed.col);
41204     },
41205
41206     /**
41207      * Starts editing the specified for the specified row/column
41208      * @param {Number} rowIndex
41209      * @param {Number} colIndex
41210      */
41211     startEditing : function(row, col){
41212         this.stopEditing();
41213         if(this.colModel.isCellEditable(col, row)){
41214             this.view.ensureVisible(row, col, true);
41215           
41216             var r = this.dataSource.getAt(row);
41217             var field = this.colModel.getDataIndex(col);
41218             var cell = Roo.get(this.view.getCell(row,col));
41219             var e = {
41220                 grid: this,
41221                 record: r,
41222                 field: field,
41223                 value: r.data[field],
41224                 row: row,
41225                 column: col,
41226                 cancel:false 
41227             };
41228             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41229                 this.editing = true;
41230                 var ed = this.colModel.getCellEditor(col, row);
41231                 
41232                 if (!ed) {
41233                     return;
41234                 }
41235                 if(!ed.rendered){
41236                     ed.render(ed.parentEl || document.body);
41237                 }
41238                 ed.field.reset();
41239                
41240                 cell.hide();
41241                 
41242                 (function(){ // complex but required for focus issues in safari, ie and opera
41243                     ed.row = row;
41244                     ed.col = col;
41245                     ed.record = r;
41246                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
41247                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
41248                     this.activeEditor = ed;
41249                     var v = r.data[field];
41250                     ed.startEdit(this.view.getCell(row, col), v);
41251                     // combo's with 'displayField and name set
41252                     if (ed.field.displayField && ed.field.name) {
41253                         ed.field.el.dom.value = r.data[ed.field.name];
41254                     }
41255                     
41256                     
41257                 }).defer(50, this);
41258             }
41259         }
41260     },
41261         
41262     /**
41263      * Stops any active editing
41264      */
41265     stopEditing : function(){
41266         if(this.activeEditor){
41267             this.activeEditor.completeEdit();
41268         }
41269         this.activeEditor = null;
41270     },
41271         
41272          /**
41273      * Called to get grid's drag proxy text, by default returns this.ddText.
41274      * @return {String}
41275      */
41276     getDragDropText : function(){
41277         var count = this.selModel.getSelectedCell() ? 1 : 0;
41278         return String.format(this.ddText, count, count == 1 ? '' : 's');
41279     }
41280         
41281 });/*
41282  * Based on:
41283  * Ext JS Library 1.1.1
41284  * Copyright(c) 2006-2007, Ext JS, LLC.
41285  *
41286  * Originally Released Under LGPL - original licence link has changed is not relivant.
41287  *
41288  * Fork - LGPL
41289  * <script type="text/javascript">
41290  */
41291
41292 // private - not really -- you end up using it !
41293 // This is a support class used internally by the Grid components
41294
41295 /**
41296  * @class Roo.grid.GridEditor
41297  * @extends Roo.Editor
41298  * Class for creating and editable grid elements.
41299  * @param {Object} config any settings (must include field)
41300  */
41301 Roo.grid.GridEditor = function(field, config){
41302     if (!config && field.field) {
41303         config = field;
41304         field = Roo.factory(config.field, Roo.form);
41305     }
41306     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41307     field.monitorTab = false;
41308 };
41309
41310 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41311     
41312     /**
41313      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41314      */
41315     
41316     alignment: "tl-tl",
41317     autoSize: "width",
41318     hideEl : false,
41319     cls: "x-small-editor x-grid-editor",
41320     shim:false,
41321     shadow:"frame"
41322 });/*
41323  * Based on:
41324  * Ext JS Library 1.1.1
41325  * Copyright(c) 2006-2007, Ext JS, LLC.
41326  *
41327  * Originally Released Under LGPL - original licence link has changed is not relivant.
41328  *
41329  * Fork - LGPL
41330  * <script type="text/javascript">
41331  */
41332   
41333
41334   
41335 Roo.grid.PropertyRecord = Roo.data.Record.create([
41336     {name:'name',type:'string'},  'value'
41337 ]);
41338
41339
41340 Roo.grid.PropertyStore = function(grid, source){
41341     this.grid = grid;
41342     this.store = new Roo.data.Store({
41343         recordType : Roo.grid.PropertyRecord
41344     });
41345     this.store.on('update', this.onUpdate,  this);
41346     if(source){
41347         this.setSource(source);
41348     }
41349     Roo.grid.PropertyStore.superclass.constructor.call(this);
41350 };
41351
41352
41353
41354 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41355     setSource : function(o){
41356         this.source = o;
41357         this.store.removeAll();
41358         var data = [];
41359         for(var k in o){
41360             if(this.isEditableValue(o[k])){
41361                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41362             }
41363         }
41364         this.store.loadRecords({records: data}, {}, true);
41365     },
41366
41367     onUpdate : function(ds, record, type){
41368         if(type == Roo.data.Record.EDIT){
41369             var v = record.data['value'];
41370             var oldValue = record.modified['value'];
41371             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41372                 this.source[record.id] = v;
41373                 record.commit();
41374                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41375             }else{
41376                 record.reject();
41377             }
41378         }
41379     },
41380
41381     getProperty : function(row){
41382        return this.store.getAt(row);
41383     },
41384
41385     isEditableValue: function(val){
41386         if(val && val instanceof Date){
41387             return true;
41388         }else if(typeof val == 'object' || typeof val == 'function'){
41389             return false;
41390         }
41391         return true;
41392     },
41393
41394     setValue : function(prop, value){
41395         this.source[prop] = value;
41396         this.store.getById(prop).set('value', value);
41397     },
41398
41399     getSource : function(){
41400         return this.source;
41401     }
41402 });
41403
41404 Roo.grid.PropertyColumnModel = function(grid, store){
41405     this.grid = grid;
41406     var g = Roo.grid;
41407     g.PropertyColumnModel.superclass.constructor.call(this, [
41408         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41409         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41410     ]);
41411     this.store = store;
41412     this.bselect = Roo.DomHelper.append(document.body, {
41413         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41414             {tag: 'option', value: 'true', html: 'true'},
41415             {tag: 'option', value: 'false', html: 'false'}
41416         ]
41417     });
41418     Roo.id(this.bselect);
41419     var f = Roo.form;
41420     this.editors = {
41421         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41422         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41423         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41424         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41425         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41426     };
41427     this.renderCellDelegate = this.renderCell.createDelegate(this);
41428     this.renderPropDelegate = this.renderProp.createDelegate(this);
41429 };
41430
41431 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41432     
41433     
41434     nameText : 'Name',
41435     valueText : 'Value',
41436     
41437     dateFormat : 'm/j/Y',
41438     
41439     
41440     renderDate : function(dateVal){
41441         return dateVal.dateFormat(this.dateFormat);
41442     },
41443
41444     renderBool : function(bVal){
41445         return bVal ? 'true' : 'false';
41446     },
41447
41448     isCellEditable : function(colIndex, rowIndex){
41449         return colIndex == 1;
41450     },
41451
41452     getRenderer : function(col){
41453         return col == 1 ?
41454             this.renderCellDelegate : this.renderPropDelegate;
41455     },
41456
41457     renderProp : function(v){
41458         return this.getPropertyName(v);
41459     },
41460
41461     renderCell : function(val){
41462         var rv = val;
41463         if(val instanceof Date){
41464             rv = this.renderDate(val);
41465         }else if(typeof val == 'boolean'){
41466             rv = this.renderBool(val);
41467         }
41468         return Roo.util.Format.htmlEncode(rv);
41469     },
41470
41471     getPropertyName : function(name){
41472         var pn = this.grid.propertyNames;
41473         return pn && pn[name] ? pn[name] : name;
41474     },
41475
41476     getCellEditor : function(colIndex, rowIndex){
41477         var p = this.store.getProperty(rowIndex);
41478         var n = p.data['name'], val = p.data['value'];
41479         
41480         if(typeof(this.grid.customEditors[n]) == 'string'){
41481             return this.editors[this.grid.customEditors[n]];
41482         }
41483         if(typeof(this.grid.customEditors[n]) != 'undefined'){
41484             return this.grid.customEditors[n];
41485         }
41486         if(val instanceof Date){
41487             return this.editors['date'];
41488         }else if(typeof val == 'number'){
41489             return this.editors['number'];
41490         }else if(typeof val == 'boolean'){
41491             return this.editors['boolean'];
41492         }else{
41493             return this.editors['string'];
41494         }
41495     }
41496 });
41497
41498 /**
41499  * @class Roo.grid.PropertyGrid
41500  * @extends Roo.grid.EditorGrid
41501  * This class represents the  interface of a component based property grid control.
41502  * <br><br>Usage:<pre><code>
41503  var grid = new Roo.grid.PropertyGrid("my-container-id", {
41504       
41505  });
41506  // set any options
41507  grid.render();
41508  * </code></pre>
41509   
41510  * @constructor
41511  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41512  * The container MUST have some type of size defined for the grid to fill. The container will be
41513  * automatically set to position relative if it isn't already.
41514  * @param {Object} config A config object that sets properties on this grid.
41515  */
41516 Roo.grid.PropertyGrid = function(container, config){
41517     config = config || {};
41518     var store = new Roo.grid.PropertyStore(this);
41519     this.store = store;
41520     var cm = new Roo.grid.PropertyColumnModel(this, store);
41521     store.store.sort('name', 'ASC');
41522     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41523         ds: store.store,
41524         cm: cm,
41525         enableColLock:false,
41526         enableColumnMove:false,
41527         stripeRows:false,
41528         trackMouseOver: false,
41529         clicksToEdit:1
41530     }, config));
41531     this.getGridEl().addClass('x-props-grid');
41532     this.lastEditRow = null;
41533     this.on('columnresize', this.onColumnResize, this);
41534     this.addEvents({
41535          /**
41536              * @event beforepropertychange
41537              * Fires before a property changes (return false to stop?)
41538              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41539              * @param {String} id Record Id
41540              * @param {String} newval New Value
41541          * @param {String} oldval Old Value
41542              */
41543         "beforepropertychange": true,
41544         /**
41545              * @event propertychange
41546              * Fires after a property changes
41547              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41548              * @param {String} id Record Id
41549              * @param {String} newval New Value
41550          * @param {String} oldval Old Value
41551              */
41552         "propertychange": true
41553     });
41554     this.customEditors = this.customEditors || {};
41555 };
41556 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41557     
41558      /**
41559      * @cfg {Object} customEditors map of colnames=> custom editors.
41560      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41561      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41562      * false disables editing of the field.
41563          */
41564     
41565       /**
41566      * @cfg {Object} propertyNames map of property Names to their displayed value
41567          */
41568     
41569     render : function(){
41570         Roo.grid.PropertyGrid.superclass.render.call(this);
41571         this.autoSize.defer(100, this);
41572     },
41573
41574     autoSize : function(){
41575         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41576         if(this.view){
41577             this.view.fitColumns();
41578         }
41579     },
41580
41581     onColumnResize : function(){
41582         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41583         this.autoSize();
41584     },
41585     /**
41586      * Sets the data for the Grid
41587      * accepts a Key => Value object of all the elements avaiable.
41588      * @param {Object} data  to appear in grid.
41589      */
41590     setSource : function(source){
41591         this.store.setSource(source);
41592         //this.autoSize();
41593     },
41594     /**
41595      * Gets all the data from the grid.
41596      * @return {Object} data  data stored in grid
41597      */
41598     getSource : function(){
41599         return this.store.getSource();
41600     }
41601 });/*
41602   
41603  * Licence LGPL
41604  
41605  */
41606  
41607 /**
41608  * @class Roo.grid.Calendar
41609  * @extends Roo.grid.Grid
41610  * This class extends the Grid to provide a calendar widget
41611  * <br><br>Usage:<pre><code>
41612  var grid = new Roo.grid.Calendar("my-container-id", {
41613      ds: myDataStore,
41614      cm: myColModel,
41615      selModel: mySelectionModel,
41616      autoSizeColumns: true,
41617      monitorWindowResize: false,
41618      trackMouseOver: true
41619      eventstore : real data store..
41620  });
41621  // set any options
41622  grid.render();
41623   
41624   * @constructor
41625  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41626  * The container MUST have some type of size defined for the grid to fill. The container will be
41627  * automatically set to position relative if it isn't already.
41628  * @param {Object} config A config object that sets properties on this grid.
41629  */
41630 Roo.grid.Calendar = function(container, config){
41631         // initialize the container
41632         this.container = Roo.get(container);
41633         this.container.update("");
41634         this.container.setStyle("overflow", "hidden");
41635     this.container.addClass('x-grid-container');
41636
41637     this.id = this.container.id;
41638
41639     Roo.apply(this, config);
41640     // check and correct shorthanded configs
41641     
41642     var rows = [];
41643     var d =1;
41644     for (var r = 0;r < 6;r++) {
41645         
41646         rows[r]=[];
41647         for (var c =0;c < 7;c++) {
41648             rows[r][c]= '';
41649         }
41650     }
41651     if (this.eventStore) {
41652         this.eventStore= Roo.factory(this.eventStore, Roo.data);
41653         this.eventStore.on('load',this.onLoad, this);
41654         this.eventStore.on('beforeload',this.clearEvents, this);
41655          
41656     }
41657     
41658     this.dataSource = new Roo.data.Store({
41659             proxy: new Roo.data.MemoryProxy(rows),
41660             reader: new Roo.data.ArrayReader({}, [
41661                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41662     });
41663
41664     this.dataSource.load();
41665     this.ds = this.dataSource;
41666     this.ds.xmodule = this.xmodule || false;
41667     
41668     
41669     var cellRender = function(v,x,r)
41670     {
41671         return String.format(
41672             '<div class="fc-day  fc-widget-content"><div>' +
41673                 '<div class="fc-event-container"></div>' +
41674                 '<div class="fc-day-number">{0}</div>'+
41675                 
41676                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41677             '</div></div>', v);
41678     
41679     }
41680     
41681     
41682     this.colModel = new Roo.grid.ColumnModel( [
41683         {
41684             xtype: 'ColumnModel',
41685             xns: Roo.grid,
41686             dataIndex : 'weekday0',
41687             header : 'Sunday',
41688             renderer : cellRender
41689         },
41690         {
41691             xtype: 'ColumnModel',
41692             xns: Roo.grid,
41693             dataIndex : 'weekday1',
41694             header : 'Monday',
41695             renderer : cellRender
41696         },
41697         {
41698             xtype: 'ColumnModel',
41699             xns: Roo.grid,
41700             dataIndex : 'weekday2',
41701             header : 'Tuesday',
41702             renderer : cellRender
41703         },
41704         {
41705             xtype: 'ColumnModel',
41706             xns: Roo.grid,
41707             dataIndex : 'weekday3',
41708             header : 'Wednesday',
41709             renderer : cellRender
41710         },
41711         {
41712             xtype: 'ColumnModel',
41713             xns: Roo.grid,
41714             dataIndex : 'weekday4',
41715             header : 'Thursday',
41716             renderer : cellRender
41717         },
41718         {
41719             xtype: 'ColumnModel',
41720             xns: Roo.grid,
41721             dataIndex : 'weekday5',
41722             header : 'Friday',
41723             renderer : cellRender
41724         },
41725         {
41726             xtype: 'ColumnModel',
41727             xns: Roo.grid,
41728             dataIndex : 'weekday6',
41729             header : 'Saturday',
41730             renderer : cellRender
41731         }
41732     ]);
41733     this.cm = this.colModel;
41734     this.cm.xmodule = this.xmodule || false;
41735  
41736         
41737           
41738     //this.selModel = new Roo.grid.CellSelectionModel();
41739     //this.sm = this.selModel;
41740     //this.selModel.init(this);
41741     
41742     
41743     if(this.width){
41744         this.container.setWidth(this.width);
41745     }
41746
41747     if(this.height){
41748         this.container.setHeight(this.height);
41749     }
41750     /** @private */
41751         this.addEvents({
41752         // raw events
41753         /**
41754          * @event click
41755          * The raw click event for the entire grid.
41756          * @param {Roo.EventObject} e
41757          */
41758         "click" : true,
41759         /**
41760          * @event dblclick
41761          * The raw dblclick event for the entire grid.
41762          * @param {Roo.EventObject} e
41763          */
41764         "dblclick" : true,
41765         /**
41766          * @event contextmenu
41767          * The raw contextmenu event for the entire grid.
41768          * @param {Roo.EventObject} e
41769          */
41770         "contextmenu" : true,
41771         /**
41772          * @event mousedown
41773          * The raw mousedown event for the entire grid.
41774          * @param {Roo.EventObject} e
41775          */
41776         "mousedown" : true,
41777         /**
41778          * @event mouseup
41779          * The raw mouseup event for the entire grid.
41780          * @param {Roo.EventObject} e
41781          */
41782         "mouseup" : true,
41783         /**
41784          * @event mouseover
41785          * The raw mouseover event for the entire grid.
41786          * @param {Roo.EventObject} e
41787          */
41788         "mouseover" : true,
41789         /**
41790          * @event mouseout
41791          * The raw mouseout event for the entire grid.
41792          * @param {Roo.EventObject} e
41793          */
41794         "mouseout" : true,
41795         /**
41796          * @event keypress
41797          * The raw keypress event for the entire grid.
41798          * @param {Roo.EventObject} e
41799          */
41800         "keypress" : true,
41801         /**
41802          * @event keydown
41803          * The raw keydown event for the entire grid.
41804          * @param {Roo.EventObject} e
41805          */
41806         "keydown" : true,
41807
41808         // custom events
41809
41810         /**
41811          * @event cellclick
41812          * Fires when a cell is clicked
41813          * @param {Grid} this
41814          * @param {Number} rowIndex
41815          * @param {Number} columnIndex
41816          * @param {Roo.EventObject} e
41817          */
41818         "cellclick" : true,
41819         /**
41820          * @event celldblclick
41821          * Fires when a cell is double clicked
41822          * @param {Grid} this
41823          * @param {Number} rowIndex
41824          * @param {Number} columnIndex
41825          * @param {Roo.EventObject} e
41826          */
41827         "celldblclick" : true,
41828         /**
41829          * @event rowclick
41830          * Fires when a row is clicked
41831          * @param {Grid} this
41832          * @param {Number} rowIndex
41833          * @param {Roo.EventObject} e
41834          */
41835         "rowclick" : true,
41836         /**
41837          * @event rowdblclick
41838          * Fires when a row is double clicked
41839          * @param {Grid} this
41840          * @param {Number} rowIndex
41841          * @param {Roo.EventObject} e
41842          */
41843         "rowdblclick" : true,
41844         /**
41845          * @event headerclick
41846          * Fires when a header is clicked
41847          * @param {Grid} this
41848          * @param {Number} columnIndex
41849          * @param {Roo.EventObject} e
41850          */
41851         "headerclick" : true,
41852         /**
41853          * @event headerdblclick
41854          * Fires when a header cell is double clicked
41855          * @param {Grid} this
41856          * @param {Number} columnIndex
41857          * @param {Roo.EventObject} e
41858          */
41859         "headerdblclick" : true,
41860         /**
41861          * @event rowcontextmenu
41862          * Fires when a row is right clicked
41863          * @param {Grid} this
41864          * @param {Number} rowIndex
41865          * @param {Roo.EventObject} e
41866          */
41867         "rowcontextmenu" : true,
41868         /**
41869          * @event cellcontextmenu
41870          * Fires when a cell is right clicked
41871          * @param {Grid} this
41872          * @param {Number} rowIndex
41873          * @param {Number} cellIndex
41874          * @param {Roo.EventObject} e
41875          */
41876          "cellcontextmenu" : true,
41877         /**
41878          * @event headercontextmenu
41879          * Fires when a header is right clicked
41880          * @param {Grid} this
41881          * @param {Number} columnIndex
41882          * @param {Roo.EventObject} e
41883          */
41884         "headercontextmenu" : true,
41885         /**
41886          * @event bodyscroll
41887          * Fires when the body element is scrolled
41888          * @param {Number} scrollLeft
41889          * @param {Number} scrollTop
41890          */
41891         "bodyscroll" : true,
41892         /**
41893          * @event columnresize
41894          * Fires when the user resizes a column
41895          * @param {Number} columnIndex
41896          * @param {Number} newSize
41897          */
41898         "columnresize" : true,
41899         /**
41900          * @event columnmove
41901          * Fires when the user moves a column
41902          * @param {Number} oldIndex
41903          * @param {Number} newIndex
41904          */
41905         "columnmove" : true,
41906         /**
41907          * @event startdrag
41908          * Fires when row(s) start being dragged
41909          * @param {Grid} this
41910          * @param {Roo.GridDD} dd The drag drop object
41911          * @param {event} e The raw browser event
41912          */
41913         "startdrag" : true,
41914         /**
41915          * @event enddrag
41916          * Fires when a drag operation is complete
41917          * @param {Grid} this
41918          * @param {Roo.GridDD} dd The drag drop object
41919          * @param {event} e The raw browser event
41920          */
41921         "enddrag" : true,
41922         /**
41923          * @event dragdrop
41924          * Fires when dragged row(s) are dropped on a valid DD target
41925          * @param {Grid} this
41926          * @param {Roo.GridDD} dd The drag drop object
41927          * @param {String} targetId The target drag drop object
41928          * @param {event} e The raw browser event
41929          */
41930         "dragdrop" : true,
41931         /**
41932          * @event dragover
41933          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41934          * @param {Grid} this
41935          * @param {Roo.GridDD} dd The drag drop object
41936          * @param {String} targetId The target drag drop object
41937          * @param {event} e The raw browser event
41938          */
41939         "dragover" : true,
41940         /**
41941          * @event dragenter
41942          *  Fires when the dragged row(s) first cross another DD target while being dragged
41943          * @param {Grid} this
41944          * @param {Roo.GridDD} dd The drag drop object
41945          * @param {String} targetId The target drag drop object
41946          * @param {event} e The raw browser event
41947          */
41948         "dragenter" : true,
41949         /**
41950          * @event dragout
41951          * Fires when the dragged row(s) leave another DD target while being dragged
41952          * @param {Grid} this
41953          * @param {Roo.GridDD} dd The drag drop object
41954          * @param {String} targetId The target drag drop object
41955          * @param {event} e The raw browser event
41956          */
41957         "dragout" : true,
41958         /**
41959          * @event rowclass
41960          * Fires when a row is rendered, so you can change add a style to it.
41961          * @param {GridView} gridview   The grid view
41962          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41963          */
41964         'rowclass' : true,
41965
41966         /**
41967          * @event render
41968          * Fires when the grid is rendered
41969          * @param {Grid} grid
41970          */
41971         'render' : true,
41972             /**
41973              * @event select
41974              * Fires when a date is selected
41975              * @param {DatePicker} this
41976              * @param {Date} date The selected date
41977              */
41978         'select': true,
41979         /**
41980              * @event monthchange
41981              * Fires when the displayed month changes 
41982              * @param {DatePicker} this
41983              * @param {Date} date The selected month
41984              */
41985         'monthchange': true,
41986         /**
41987              * @event evententer
41988              * Fires when mouse over an event
41989              * @param {Calendar} this
41990              * @param {event} Event
41991              */
41992         'evententer': true,
41993         /**
41994              * @event eventleave
41995              * Fires when the mouse leaves an
41996              * @param {Calendar} this
41997              * @param {event}
41998              */
41999         'eventleave': true,
42000         /**
42001              * @event eventclick
42002              * Fires when the mouse click an
42003              * @param {Calendar} this
42004              * @param {event}
42005              */
42006         'eventclick': true,
42007         /**
42008              * @event eventrender
42009              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42010              * @param {Calendar} this
42011              * @param {data} data to be modified
42012              */
42013         'eventrender': true
42014         
42015     });
42016
42017     Roo.grid.Grid.superclass.constructor.call(this);
42018     this.on('render', function() {
42019         this.view.el.addClass('x-grid-cal'); 
42020         
42021         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42022
42023     },this);
42024     
42025     if (!Roo.grid.Calendar.style) {
42026         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42027             
42028             
42029             '.x-grid-cal .x-grid-col' :  {
42030                 height: 'auto !important',
42031                 'vertical-align': 'top'
42032             },
42033             '.x-grid-cal  .fc-event-hori' : {
42034                 height: '14px'
42035             }
42036              
42037             
42038         }, Roo.id());
42039     }
42040
42041     
42042     
42043 };
42044 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42045     /**
42046      * @cfg {Store} eventStore The store that loads events.
42047      */
42048     eventStore : 25,
42049
42050      
42051     activeDate : false,
42052     startDay : 0,
42053     autoWidth : true,
42054     monitorWindowResize : false,
42055
42056     
42057     resizeColumns : function() {
42058         var col = (this.view.el.getWidth() / 7) - 3;
42059         // loop through cols, and setWidth
42060         for(var i =0 ; i < 7 ; i++){
42061             this.cm.setColumnWidth(i, col);
42062         }
42063     },
42064      setDate :function(date) {
42065         
42066         Roo.log('setDate?');
42067         
42068         this.resizeColumns();
42069         var vd = this.activeDate;
42070         this.activeDate = date;
42071 //        if(vd && this.el){
42072 //            var t = date.getTime();
42073 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42074 //                Roo.log('using add remove');
42075 //                
42076 //                this.fireEvent('monthchange', this, date);
42077 //                
42078 //                this.cells.removeClass("fc-state-highlight");
42079 //                this.cells.each(function(c){
42080 //                   if(c.dateValue == t){
42081 //                       c.addClass("fc-state-highlight");
42082 //                       setTimeout(function(){
42083 //                            try{c.dom.firstChild.focus();}catch(e){}
42084 //                       }, 50);
42085 //                       return false;
42086 //                   }
42087 //                   return true;
42088 //                });
42089 //                return;
42090 //            }
42091 //        }
42092         
42093         var days = date.getDaysInMonth();
42094         
42095         var firstOfMonth = date.getFirstDateOfMonth();
42096         var startingPos = firstOfMonth.getDay()-this.startDay;
42097         
42098         if(startingPos < this.startDay){
42099             startingPos += 7;
42100         }
42101         
42102         var pm = date.add(Date.MONTH, -1);
42103         var prevStart = pm.getDaysInMonth()-startingPos;
42104 //        
42105         
42106         
42107         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42108         
42109         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42110         //this.cells.addClassOnOver('fc-state-hover');
42111         
42112         var cells = this.cells.elements;
42113         var textEls = this.textNodes;
42114         
42115         //Roo.each(cells, function(cell){
42116         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42117         //});
42118         
42119         days += startingPos;
42120
42121         // convert everything to numbers so it's fast
42122         var day = 86400000;
42123         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42124         //Roo.log(d);
42125         //Roo.log(pm);
42126         //Roo.log(prevStart);
42127         
42128         var today = new Date().clearTime().getTime();
42129         var sel = date.clearTime().getTime();
42130         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42131         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42132         var ddMatch = this.disabledDatesRE;
42133         var ddText = this.disabledDatesText;
42134         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42135         var ddaysText = this.disabledDaysText;
42136         var format = this.format;
42137         
42138         var setCellClass = function(cal, cell){
42139             
42140             //Roo.log('set Cell Class');
42141             cell.title = "";
42142             var t = d.getTime();
42143             
42144             //Roo.log(d);
42145             
42146             
42147             cell.dateValue = t;
42148             if(t == today){
42149                 cell.className += " fc-today";
42150                 cell.className += " fc-state-highlight";
42151                 cell.title = cal.todayText;
42152             }
42153             if(t == sel){
42154                 // disable highlight in other month..
42155                 cell.className += " fc-state-highlight";
42156                 
42157             }
42158             // disabling
42159             if(t < min) {
42160                 //cell.className = " fc-state-disabled";
42161                 cell.title = cal.minText;
42162                 return;
42163             }
42164             if(t > max) {
42165                 //cell.className = " fc-state-disabled";
42166                 cell.title = cal.maxText;
42167                 return;
42168             }
42169             if(ddays){
42170                 if(ddays.indexOf(d.getDay()) != -1){
42171                     // cell.title = ddaysText;
42172                    // cell.className = " fc-state-disabled";
42173                 }
42174             }
42175             if(ddMatch && format){
42176                 var fvalue = d.dateFormat(format);
42177                 if(ddMatch.test(fvalue)){
42178                     cell.title = ddText.replace("%0", fvalue);
42179                    cell.className = " fc-state-disabled";
42180                 }
42181             }
42182             
42183             if (!cell.initialClassName) {
42184                 cell.initialClassName = cell.dom.className;
42185             }
42186             
42187             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
42188         };
42189
42190         var i = 0;
42191         
42192         for(; i < startingPos; i++) {
42193             cells[i].dayName =  (++prevStart);
42194             Roo.log(textEls[i]);
42195             d.setDate(d.getDate()+1);
42196             
42197             //cells[i].className = "fc-past fc-other-month";
42198             setCellClass(this, cells[i]);
42199         }
42200         
42201         var intDay = 0;
42202         
42203         for(; i < days; i++){
42204             intDay = i - startingPos + 1;
42205             cells[i].dayName =  (intDay);
42206             d.setDate(d.getDate()+1);
42207             
42208             cells[i].className = ''; // "x-date-active";
42209             setCellClass(this, cells[i]);
42210         }
42211         var extraDays = 0;
42212         
42213         for(; i < 42; i++) {
42214             //textEls[i].innerHTML = (++extraDays);
42215             
42216             d.setDate(d.getDate()+1);
42217             cells[i].dayName = (++extraDays);
42218             cells[i].className = "fc-future fc-other-month";
42219             setCellClass(this, cells[i]);
42220         }
42221         
42222         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42223         
42224         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42225         
42226         // this will cause all the cells to mis
42227         var rows= [];
42228         var i =0;
42229         for (var r = 0;r < 6;r++) {
42230             for (var c =0;c < 7;c++) {
42231                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42232             }    
42233         }
42234         
42235         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42236         for(i=0;i<cells.length;i++) {
42237             
42238             this.cells.elements[i].dayName = cells[i].dayName ;
42239             this.cells.elements[i].className = cells[i].className;
42240             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42241             this.cells.elements[i].title = cells[i].title ;
42242             this.cells.elements[i].dateValue = cells[i].dateValue ;
42243         }
42244         
42245         
42246         
42247         
42248         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42249         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42250         
42251         ////if(totalRows != 6){
42252             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42253            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42254        // }
42255         
42256         this.fireEvent('monthchange', this, date);
42257         
42258         
42259     },
42260  /**
42261      * Returns the grid's SelectionModel.
42262      * @return {SelectionModel}
42263      */
42264     getSelectionModel : function(){
42265         if(!this.selModel){
42266             this.selModel = new Roo.grid.CellSelectionModel();
42267         }
42268         return this.selModel;
42269     },
42270
42271     load: function() {
42272         this.eventStore.load()
42273         
42274         
42275         
42276     },
42277     
42278     findCell : function(dt) {
42279         dt = dt.clearTime().getTime();
42280         var ret = false;
42281         this.cells.each(function(c){
42282             //Roo.log("check " +c.dateValue + '?=' + dt);
42283             if(c.dateValue == dt){
42284                 ret = c;
42285                 return false;
42286             }
42287             return true;
42288         });
42289         
42290         return ret;
42291     },
42292     
42293     findCells : function(rec) {
42294         var s = rec.data.start_dt.clone().clearTime().getTime();
42295        // Roo.log(s);
42296         var e= rec.data.end_dt.clone().clearTime().getTime();
42297        // Roo.log(e);
42298         var ret = [];
42299         this.cells.each(function(c){
42300              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42301             
42302             if(c.dateValue > e){
42303                 return ;
42304             }
42305             if(c.dateValue < s){
42306                 return ;
42307             }
42308             ret.push(c);
42309         });
42310         
42311         return ret;    
42312     },
42313     
42314     findBestRow: function(cells)
42315     {
42316         var ret = 0;
42317         
42318         for (var i =0 ; i < cells.length;i++) {
42319             ret  = Math.max(cells[i].rows || 0,ret);
42320         }
42321         return ret;
42322         
42323     },
42324     
42325     
42326     addItem : function(rec)
42327     {
42328         // look for vertical location slot in
42329         var cells = this.findCells(rec);
42330         
42331         rec.row = this.findBestRow(cells);
42332         
42333         // work out the location.
42334         
42335         var crow = false;
42336         var rows = [];
42337         for(var i =0; i < cells.length; i++) {
42338             if (!crow) {
42339                 crow = {
42340                     start : cells[i],
42341                     end :  cells[i]
42342                 };
42343                 continue;
42344             }
42345             if (crow.start.getY() == cells[i].getY()) {
42346                 // on same row.
42347                 crow.end = cells[i];
42348                 continue;
42349             }
42350             // different row.
42351             rows.push(crow);
42352             crow = {
42353                 start: cells[i],
42354                 end : cells[i]
42355             };
42356             
42357         }
42358         
42359         rows.push(crow);
42360         rec.els = [];
42361         rec.rows = rows;
42362         rec.cells = cells;
42363         for (var i = 0; i < cells.length;i++) {
42364             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42365             
42366         }
42367         
42368         
42369     },
42370     
42371     clearEvents: function() {
42372         
42373         if (!this.eventStore.getCount()) {
42374             return;
42375         }
42376         // reset number of rows in cells.
42377         Roo.each(this.cells.elements, function(c){
42378             c.rows = 0;
42379         });
42380         
42381         this.eventStore.each(function(e) {
42382             this.clearEvent(e);
42383         },this);
42384         
42385     },
42386     
42387     clearEvent : function(ev)
42388     {
42389         if (ev.els) {
42390             Roo.each(ev.els, function(el) {
42391                 el.un('mouseenter' ,this.onEventEnter, this);
42392                 el.un('mouseleave' ,this.onEventLeave, this);
42393                 el.remove();
42394             },this);
42395             ev.els = [];
42396         }
42397     },
42398     
42399     
42400     renderEvent : function(ev,ctr) {
42401         if (!ctr) {
42402              ctr = this.view.el.select('.fc-event-container',true).first();
42403         }
42404         
42405          
42406         this.clearEvent(ev);
42407             //code
42408        
42409         
42410         
42411         ev.els = [];
42412         var cells = ev.cells;
42413         var rows = ev.rows;
42414         this.fireEvent('eventrender', this, ev);
42415         
42416         for(var i =0; i < rows.length; i++) {
42417             
42418             cls = '';
42419             if (i == 0) {
42420                 cls += ' fc-event-start';
42421             }
42422             if ((i+1) == rows.length) {
42423                 cls += ' fc-event-end';
42424             }
42425             
42426             //Roo.log(ev.data);
42427             // how many rows should it span..
42428             var cg = this.eventTmpl.append(ctr,Roo.apply({
42429                 fccls : cls
42430                 
42431             }, ev.data) , true);
42432             
42433             
42434             cg.on('mouseenter' ,this.onEventEnter, this, ev);
42435             cg.on('mouseleave' ,this.onEventLeave, this, ev);
42436             cg.on('click', this.onEventClick, this, ev);
42437             
42438             ev.els.push(cg);
42439             
42440             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42441             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42442             //Roo.log(cg);
42443              
42444             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
42445             cg.setWidth(ebox.right - sbox.x -2);
42446         }
42447     },
42448     
42449     renderEvents: function()
42450     {   
42451         // first make sure there is enough space..
42452         
42453         if (!this.eventTmpl) {
42454             this.eventTmpl = new Roo.Template(
42455                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
42456                     '<div class="fc-event-inner">' +
42457                         '<span class="fc-event-time">{time}</span>' +
42458                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42459                     '</div>' +
42460                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
42461                 '</div>'
42462             );
42463                 
42464         }
42465                
42466         
42467         
42468         this.cells.each(function(c) {
42469             //Roo.log(c.select('.fc-day-content div',true).first());
42470             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42471         });
42472         
42473         var ctr = this.view.el.select('.fc-event-container',true).first();
42474         
42475         var cls;
42476         this.eventStore.each(function(ev){
42477             
42478             this.renderEvent(ev);
42479              
42480              
42481         }, this);
42482         this.view.layout();
42483         
42484     },
42485     
42486     onEventEnter: function (e, el,event,d) {
42487         this.fireEvent('evententer', this, el, event);
42488     },
42489     
42490     onEventLeave: function (e, el,event,d) {
42491         this.fireEvent('eventleave', this, el, event);
42492     },
42493     
42494     onEventClick: function (e, el,event,d) {
42495         this.fireEvent('eventclick', this, el, event);
42496     },
42497     
42498     onMonthChange: function () {
42499         this.store.load();
42500     },
42501     
42502     onLoad: function () {
42503         
42504         //Roo.log('calendar onload');
42505 //         
42506         if(this.eventStore.getCount() > 0){
42507             
42508            
42509             
42510             this.eventStore.each(function(d){
42511                 
42512                 
42513                 // FIXME..
42514                 var add =   d.data;
42515                 if (typeof(add.end_dt) == 'undefined')  {
42516                     Roo.log("Missing End time in calendar data: ");
42517                     Roo.log(d);
42518                     return;
42519                 }
42520                 if (typeof(add.start_dt) == 'undefined')  {
42521                     Roo.log("Missing Start time in calendar data: ");
42522                     Roo.log(d);
42523                     return;
42524                 }
42525                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42526                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42527                 add.id = add.id || d.id;
42528                 add.title = add.title || '??';
42529                 
42530                 this.addItem(d);
42531                 
42532              
42533             },this);
42534         }
42535         
42536         this.renderEvents();
42537     }
42538     
42539
42540 });
42541 /*
42542  grid : {
42543                 xtype: 'Grid',
42544                 xns: Roo.grid,
42545                 listeners : {
42546                     render : function ()
42547                     {
42548                         _this.grid = this;
42549                         
42550                         if (!this.view.el.hasClass('course-timesheet')) {
42551                             this.view.el.addClass('course-timesheet');
42552                         }
42553                         if (this.tsStyle) {
42554                             this.ds.load({});
42555                             return; 
42556                         }
42557                         Roo.log('width');
42558                         Roo.log(_this.grid.view.el.getWidth());
42559                         
42560                         
42561                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
42562                             '.course-timesheet .x-grid-row' : {
42563                                 height: '80px'
42564                             },
42565                             '.x-grid-row td' : {
42566                                 'vertical-align' : 0
42567                             },
42568                             '.course-edit-link' : {
42569                                 'color' : 'blue',
42570                                 'text-overflow' : 'ellipsis',
42571                                 'overflow' : 'hidden',
42572                                 'white-space' : 'nowrap',
42573                                 'cursor' : 'pointer'
42574                             },
42575                             '.sub-link' : {
42576                                 'color' : 'green'
42577                             },
42578                             '.de-act-sup-link' : {
42579                                 'color' : 'purple',
42580                                 'text-decoration' : 'line-through'
42581                             },
42582                             '.de-act-link' : {
42583                                 'color' : 'red',
42584                                 'text-decoration' : 'line-through'
42585                             },
42586                             '.course-timesheet .course-highlight' : {
42587                                 'border-top-style': 'dashed !important',
42588                                 'border-bottom-bottom': 'dashed !important'
42589                             },
42590                             '.course-timesheet .course-item' : {
42591                                 'font-family'   : 'tahoma, arial, helvetica',
42592                                 'font-size'     : '11px',
42593                                 'overflow'      : 'hidden',
42594                                 'padding-left'  : '10px',
42595                                 'padding-right' : '10px',
42596                                 'padding-top' : '10px' 
42597                             }
42598                             
42599                         }, Roo.id());
42600                                 this.ds.load({});
42601                     }
42602                 },
42603                 autoWidth : true,
42604                 monitorWindowResize : false,
42605                 cellrenderer : function(v,x,r)
42606                 {
42607                     return v;
42608                 },
42609                 sm : {
42610                     xtype: 'CellSelectionModel',
42611                     xns: Roo.grid
42612                 },
42613                 dataSource : {
42614                     xtype: 'Store',
42615                     xns: Roo.data,
42616                     listeners : {
42617                         beforeload : function (_self, options)
42618                         {
42619                             options.params = options.params || {};
42620                             options.params._month = _this.monthField.getValue();
42621                             options.params.limit = 9999;
42622                             options.params['sort'] = 'when_dt';    
42623                             options.params['dir'] = 'ASC';    
42624                             this.proxy.loadResponse = this.loadResponse;
42625                             Roo.log("load?");
42626                             //this.addColumns();
42627                         },
42628                         load : function (_self, records, options)
42629                         {
42630                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42631                                 // if you click on the translation.. you can edit it...
42632                                 var el = Roo.get(this);
42633                                 var id = el.dom.getAttribute('data-id');
42634                                 var d = el.dom.getAttribute('data-date');
42635                                 var t = el.dom.getAttribute('data-time');
42636                                 //var id = this.child('span').dom.textContent;
42637                                 
42638                                 //Roo.log(this);
42639                                 Pman.Dialog.CourseCalendar.show({
42640                                     id : id,
42641                                     when_d : d,
42642                                     when_t : t,
42643                                     productitem_active : id ? 1 : 0
42644                                 }, function() {
42645                                     _this.grid.ds.load({});
42646                                 });
42647                            
42648                            });
42649                            
42650                            _this.panel.fireEvent('resize', [ '', '' ]);
42651                         }
42652                     },
42653                     loadResponse : function(o, success, response){
42654                             // this is overridden on before load..
42655                             
42656                             Roo.log("our code?");       
42657                             //Roo.log(success);
42658                             //Roo.log(response)
42659                             delete this.activeRequest;
42660                             if(!success){
42661                                 this.fireEvent("loadexception", this, o, response);
42662                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42663                                 return;
42664                             }
42665                             var result;
42666                             try {
42667                                 result = o.reader.read(response);
42668                             }catch(e){
42669                                 Roo.log("load exception?");
42670                                 this.fireEvent("loadexception", this, o, response, e);
42671                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42672                                 return;
42673                             }
42674                             Roo.log("ready...");        
42675                             // loop through result.records;
42676                             // and set this.tdate[date] = [] << array of records..
42677                             _this.tdata  = {};
42678                             Roo.each(result.records, function(r){
42679                                 //Roo.log(r.data);
42680                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42681                                     _this.tdata[r.data.when_dt.format('j')] = [];
42682                                 }
42683                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42684                             });
42685                             
42686                             //Roo.log(_this.tdata);
42687                             
42688                             result.records = [];
42689                             result.totalRecords = 6;
42690                     
42691                             // let's generate some duumy records for the rows.
42692                             //var st = _this.dateField.getValue();
42693                             
42694                             // work out monday..
42695                             //st = st.add(Date.DAY, -1 * st.format('w'));
42696                             
42697                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42698                             
42699                             var firstOfMonth = date.getFirstDayOfMonth();
42700                             var days = date.getDaysInMonth();
42701                             var d = 1;
42702                             var firstAdded = false;
42703                             for (var i = 0; i < result.totalRecords ; i++) {
42704                                 //var d= st.add(Date.DAY, i);
42705                                 var row = {};
42706                                 var added = 0;
42707                                 for(var w = 0 ; w < 7 ; w++){
42708                                     if(!firstAdded && firstOfMonth != w){
42709                                         continue;
42710                                     }
42711                                     if(d > days){
42712                                         continue;
42713                                     }
42714                                     firstAdded = true;
42715                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
42716                                     row['weekday'+w] = String.format(
42717                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
42718                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42719                                                     d,
42720                                                     date.format('Y-m-')+dd
42721                                                 );
42722                                     added++;
42723                                     if(typeof(_this.tdata[d]) != 'undefined'){
42724                                         Roo.each(_this.tdata[d], function(r){
42725                                             var is_sub = '';
42726                                             var deactive = '';
42727                                             var id = r.id;
42728                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
42729                                             if(r.parent_id*1>0){
42730                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
42731                                                 id = r.parent_id;
42732                                             }
42733                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
42734                                                 deactive = 'de-act-link';
42735                                             }
42736                                             
42737                                             row['weekday'+w] += String.format(
42738                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42739                                                     id, //0
42740                                                     r.product_id_name, //1
42741                                                     r.when_dt.format('h:ia'), //2
42742                                                     is_sub, //3
42743                                                     deactive, //4
42744                                                     desc // 5
42745                                             );
42746                                         });
42747                                     }
42748                                     d++;
42749                                 }
42750                                 
42751                                 // only do this if something added..
42752                                 if(added > 0){ 
42753                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
42754                                 }
42755                                 
42756                                 
42757                                 // push it twice. (second one with an hour..
42758                                 
42759                             }
42760                             //Roo.log(result);
42761                             this.fireEvent("load", this, o, o.request.arg);
42762                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42763                         },
42764                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42765                     proxy : {
42766                         xtype: 'HttpProxy',
42767                         xns: Roo.data,
42768                         method : 'GET',
42769                         url : baseURL + '/Roo/Shop_course.php'
42770                     },
42771                     reader : {
42772                         xtype: 'JsonReader',
42773                         xns: Roo.data,
42774                         id : 'id',
42775                         fields : [
42776                             {
42777                                 'name': 'id',
42778                                 'type': 'int'
42779                             },
42780                             {
42781                                 'name': 'when_dt',
42782                                 'type': 'string'
42783                             },
42784                             {
42785                                 'name': 'end_dt',
42786                                 'type': 'string'
42787                             },
42788                             {
42789                                 'name': 'parent_id',
42790                                 'type': 'int'
42791                             },
42792                             {
42793                                 'name': 'product_id',
42794                                 'type': 'int'
42795                             },
42796                             {
42797                                 'name': 'productitem_id',
42798                                 'type': 'int'
42799                             },
42800                             {
42801                                 'name': 'guid',
42802                                 'type': 'int'
42803                             }
42804                         ]
42805                     }
42806                 },
42807                 toolbar : {
42808                     xtype: 'Toolbar',
42809                     xns: Roo,
42810                     items : [
42811                         {
42812                             xtype: 'Button',
42813                             xns: Roo.Toolbar,
42814                             listeners : {
42815                                 click : function (_self, e)
42816                                 {
42817                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42818                                     sd.setMonth(sd.getMonth()-1);
42819                                     _this.monthField.setValue(sd.format('Y-m-d'));
42820                                     _this.grid.ds.load({});
42821                                 }
42822                             },
42823                             text : "Back"
42824                         },
42825                         {
42826                             xtype: 'Separator',
42827                             xns: Roo.Toolbar
42828                         },
42829                         {
42830                             xtype: 'MonthField',
42831                             xns: Roo.form,
42832                             listeners : {
42833                                 render : function (_self)
42834                                 {
42835                                     _this.monthField = _self;
42836                                    // _this.monthField.set  today
42837                                 },
42838                                 select : function (combo, date)
42839                                 {
42840                                     _this.grid.ds.load({});
42841                                 }
42842                             },
42843                             value : (function() { return new Date(); })()
42844                         },
42845                         {
42846                             xtype: 'Separator',
42847                             xns: Roo.Toolbar
42848                         },
42849                         {
42850                             xtype: 'TextItem',
42851                             xns: Roo.Toolbar,
42852                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42853                         },
42854                         {
42855                             xtype: 'Fill',
42856                             xns: Roo.Toolbar
42857                         },
42858                         {
42859                             xtype: 'Button',
42860                             xns: Roo.Toolbar,
42861                             listeners : {
42862                                 click : function (_self, e)
42863                                 {
42864                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42865                                     sd.setMonth(sd.getMonth()+1);
42866                                     _this.monthField.setValue(sd.format('Y-m-d'));
42867                                     _this.grid.ds.load({});
42868                                 }
42869                             },
42870                             text : "Next"
42871                         }
42872                     ]
42873                 },
42874                  
42875             }
42876         };
42877         
42878         *//*
42879  * Based on:
42880  * Ext JS Library 1.1.1
42881  * Copyright(c) 2006-2007, Ext JS, LLC.
42882  *
42883  * Originally Released Under LGPL - original licence link has changed is not relivant.
42884  *
42885  * Fork - LGPL
42886  * <script type="text/javascript">
42887  */
42888  
42889 /**
42890  * @class Roo.LoadMask
42891  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42892  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42893  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42894  * element's UpdateManager load indicator and will be destroyed after the initial load.
42895  * @constructor
42896  * Create a new LoadMask
42897  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42898  * @param {Object} config The config object
42899  */
42900 Roo.LoadMask = function(el, config){
42901     this.el = Roo.get(el);
42902     Roo.apply(this, config);
42903     if(this.store){
42904         this.store.on('beforeload', this.onBeforeLoad, this);
42905         this.store.on('load', this.onLoad, this);
42906         this.store.on('loadexception', this.onLoadException, this);
42907         this.removeMask = false;
42908     }else{
42909         var um = this.el.getUpdateManager();
42910         um.showLoadIndicator = false; // disable the default indicator
42911         um.on('beforeupdate', this.onBeforeLoad, this);
42912         um.on('update', this.onLoad, this);
42913         um.on('failure', this.onLoad, this);
42914         this.removeMask = true;
42915     }
42916 };
42917
42918 Roo.LoadMask.prototype = {
42919     /**
42920      * @cfg {Boolean} removeMask
42921      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42922      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42923      */
42924     removeMask : false,
42925     /**
42926      * @cfg {String} msg
42927      * The text to display in a centered loading message box (defaults to 'Loading...')
42928      */
42929     msg : 'Loading...',
42930     /**
42931      * @cfg {String} msgCls
42932      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42933      */
42934     msgCls : 'x-mask-loading',
42935
42936     /**
42937      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42938      * @type Boolean
42939      */
42940     disabled: false,
42941
42942     /**
42943      * Disables the mask to prevent it from being displayed
42944      */
42945     disable : function(){
42946        this.disabled = true;
42947     },
42948
42949     /**
42950      * Enables the mask so that it can be displayed
42951      */
42952     enable : function(){
42953         this.disabled = false;
42954     },
42955     
42956     onLoadException : function()
42957     {
42958         Roo.log(arguments);
42959         
42960         if (typeof(arguments[3]) != 'undefined') {
42961             Roo.MessageBox.alert("Error loading",arguments[3]);
42962         } 
42963         /*
42964         try {
42965             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42966                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42967             }   
42968         } catch(e) {
42969             
42970         }
42971         */
42972     
42973         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
42974     },
42975     // private
42976     onLoad : function()
42977     {
42978         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
42979     },
42980
42981     // private
42982     onBeforeLoad : function(){
42983         if(!this.disabled){
42984             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
42985         }
42986     },
42987
42988     // private
42989     destroy : function(){
42990         if(this.store){
42991             this.store.un('beforeload', this.onBeforeLoad, this);
42992             this.store.un('load', this.onLoad, this);
42993             this.store.un('loadexception', this.onLoadException, this);
42994         }else{
42995             var um = this.el.getUpdateManager();
42996             um.un('beforeupdate', this.onBeforeLoad, this);
42997             um.un('update', this.onLoad, this);
42998             um.un('failure', this.onLoad, this);
42999         }
43000     }
43001 };/*
43002  * Based on:
43003  * Ext JS Library 1.1.1
43004  * Copyright(c) 2006-2007, Ext JS, LLC.
43005  *
43006  * Originally Released Under LGPL - original licence link has changed is not relivant.
43007  *
43008  * Fork - LGPL
43009  * <script type="text/javascript">
43010  */
43011
43012
43013 /**
43014  * @class Roo.XTemplate
43015  * @extends Roo.Template
43016  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43017 <pre><code>
43018 var t = new Roo.XTemplate(
43019         '&lt;select name="{name}"&gt;',
43020                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
43021         '&lt;/select&gt;'
43022 );
43023  
43024 // then append, applying the master template values
43025  </code></pre>
43026  *
43027  * Supported features:
43028  *
43029  *  Tags:
43030
43031 <pre><code>
43032       {a_variable} - output encoded.
43033       {a_variable.format:("Y-m-d")} - call a method on the variable
43034       {a_variable:raw} - unencoded output
43035       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43036       {a_variable:this.method_on_template(...)} - call a method on the template object.
43037  
43038 </code></pre>
43039  *  The tpl tag:
43040 <pre><code>
43041         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
43042         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
43043         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
43044         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
43045   
43046         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
43047         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
43048 </code></pre>
43049  *      
43050  */
43051 Roo.XTemplate = function()
43052 {
43053     Roo.XTemplate.superclass.constructor.apply(this, arguments);
43054     if (this.html) {
43055         this.compile();
43056     }
43057 };
43058
43059
43060 Roo.extend(Roo.XTemplate, Roo.Template, {
43061
43062     /**
43063      * The various sub templates
43064      */
43065     tpls : false,
43066     /**
43067      *
43068      * basic tag replacing syntax
43069      * WORD:WORD()
43070      *
43071      * // you can fake an object call by doing this
43072      *  x.t:(test,tesT) 
43073      * 
43074      */
43075     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43076
43077     /**
43078      * compile the template
43079      *
43080      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43081      *
43082      */
43083     compile: function()
43084     {
43085         var s = this.html;
43086      
43087         s = ['<tpl>', s, '</tpl>'].join('');
43088     
43089         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43090             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43091             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
43092             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43093             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
43094             m,
43095             id     = 0,
43096             tpls   = [];
43097     
43098         while(true == !!(m = s.match(re))){
43099             var forMatch   = m[0].match(nameRe),
43100                 ifMatch   = m[0].match(ifRe),
43101                 execMatch   = m[0].match(execRe),
43102                 namedMatch   = m[0].match(namedRe),
43103                 
43104                 exp  = null, 
43105                 fn   = null,
43106                 exec = null,
43107                 name = forMatch && forMatch[1] ? forMatch[1] : '';
43108                 
43109             if (ifMatch) {
43110                 // if - puts fn into test..
43111                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43112                 if(exp){
43113                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43114                 }
43115             }
43116             
43117             if (execMatch) {
43118                 // exec - calls a function... returns empty if true is  returned.
43119                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43120                 if(exp){
43121                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43122                 }
43123             }
43124             
43125             
43126             if (name) {
43127                 // for = 
43128                 switch(name){
43129                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43130                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43131                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43132                 }
43133             }
43134             var uid = namedMatch ? namedMatch[1] : id;
43135             
43136             
43137             tpls.push({
43138                 id:     namedMatch ? namedMatch[1] : id,
43139                 target: name,
43140                 exec:   exec,
43141                 test:   fn,
43142                 body:   m[1] || ''
43143             });
43144             if (namedMatch) {
43145                 s = s.replace(m[0], '');
43146             } else { 
43147                 s = s.replace(m[0], '{xtpl'+ id + '}');
43148             }
43149             ++id;
43150         }
43151         this.tpls = [];
43152         for(var i = tpls.length-1; i >= 0; --i){
43153             this.compileTpl(tpls[i]);
43154             this.tpls[tpls[i].id] = tpls[i];
43155         }
43156         this.master = tpls[tpls.length-1];
43157         return this;
43158     },
43159     /**
43160      * same as applyTemplate, except it's done to one of the subTemplates
43161      * when using named templates, you can do:
43162      *
43163      * var str = pl.applySubTemplate('your-name', values);
43164      *
43165      * 
43166      * @param {Number} id of the template
43167      * @param {Object} values to apply to template
43168      * @param {Object} parent (normaly the instance of this object)
43169      */
43170     applySubTemplate : function(id, values, parent)
43171     {
43172         
43173         
43174         var t = this.tpls[id];
43175         
43176         
43177         try { 
43178             if(t.test && !t.test.call(this, values, parent)){
43179                 return '';
43180             }
43181         } catch(e) {
43182             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43183             Roo.log(e.toString());
43184             Roo.log(t.test);
43185             return ''
43186         }
43187         try { 
43188             
43189             if(t.exec && t.exec.call(this, values, parent)){
43190                 return '';
43191             }
43192         } catch(e) {
43193             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43194             Roo.log(e.toString());
43195             Roo.log(t.exec);
43196             return ''
43197         }
43198         try {
43199             var vs = t.target ? t.target.call(this, values, parent) : values;
43200             parent = t.target ? values : parent;
43201             if(t.target && vs instanceof Array){
43202                 var buf = [];
43203                 for(var i = 0, len = vs.length; i < len; i++){
43204                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
43205                 }
43206                 return buf.join('');
43207             }
43208             return t.compiled.call(this, vs, parent);
43209         } catch (e) {
43210             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43211             Roo.log(e.toString());
43212             Roo.log(t.compiled);
43213             return '';
43214         }
43215     },
43216
43217     compileTpl : function(tpl)
43218     {
43219         var fm = Roo.util.Format;
43220         var useF = this.disableFormats !== true;
43221         var sep = Roo.isGecko ? "+" : ",";
43222         var undef = function(str) {
43223             Roo.log("Property not found :"  + str);
43224             return '';
43225         };
43226         
43227         var fn = function(m, name, format, args)
43228         {
43229             //Roo.log(arguments);
43230             args = args ? args.replace(/\\'/g,"'") : args;
43231             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43232             if (typeof(format) == 'undefined') {
43233                 format= 'htmlEncode';
43234             }
43235             if (format == 'raw' ) {
43236                 format = false;
43237             }
43238             
43239             if(name.substr(0, 4) == 'xtpl'){
43240                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43241             }
43242             
43243             // build an array of options to determine if value is undefined..
43244             
43245             // basically get 'xxxx.yyyy' then do
43246             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43247             //    (function () { Roo.log("Property not found"); return ''; })() :
43248             //    ......
43249             
43250             var udef_ar = [];
43251             var lookfor = '';
43252             Roo.each(name.split('.'), function(st) {
43253                 lookfor += (lookfor.length ? '.': '') + st;
43254                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
43255             });
43256             
43257             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43258             
43259             
43260             if(format && useF){
43261                 
43262                 args = args ? ',' + args : "";
43263                  
43264                 if(format.substr(0, 5) != "this."){
43265                     format = "fm." + format + '(';
43266                 }else{
43267                     format = 'this.call("'+ format.substr(5) + '", ';
43268                     args = ", values";
43269                 }
43270                 
43271                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
43272             }
43273              
43274             if (args.length) {
43275                 // called with xxyx.yuu:(test,test)
43276                 // change to ()
43277                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
43278             }
43279             // raw.. - :raw modifier..
43280             return "'"+ sep + udef_st  + name + ")"+sep+"'";
43281             
43282         };
43283         var body;
43284         // branched to use + in gecko and [].join() in others
43285         if(Roo.isGecko){
43286             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
43287                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43288                     "';};};";
43289         }else{
43290             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
43291             body.push(tpl.body.replace(/(\r\n|\n)/g,
43292                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43293             body.push("'].join('');};};");
43294             body = body.join('');
43295         }
43296         
43297         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43298        
43299         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
43300         eval(body);
43301         
43302         return this;
43303     },
43304
43305     applyTemplate : function(values){
43306         return this.master.compiled.call(this, values, {});
43307         //var s = this.subs;
43308     },
43309
43310     apply : function(){
43311         return this.applyTemplate.apply(this, arguments);
43312     }
43313
43314  });
43315
43316 Roo.XTemplate.from = function(el){
43317     el = Roo.getDom(el);
43318     return new Roo.XTemplate(el.value || el.innerHTML);
43319 };