Roo/UndoManager.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @static
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
670      * <pre>
671                 {
672                     data : data,  // array of key=>value data like JsonReader
673                     total : data.length,
674                     success : true
675                     
676                 }
677         </pre>
678             }.</li>
679      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
680      * passed the following arguments:<ul>
681      * <li>r : Roo.data.Record[]</li>
682      * <li>options: Options object from the load call</li>
683      * <li>success: Boolean success indicator</li></ul></li>
684      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
685      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
686      * </ul>
687      */
688     load : function(options){
689         options = options || {};
690         if(this.fireEvent("beforeload", this, options) !== false){
691             this.storeOptions(options);
692             var p = Roo.apply(options.params || {}, this.baseParams);
693             // if meta was not loaded from remote source.. try requesting it.
694             if (!this.reader.metaFromRemote) {
695                 p._requestMeta = 1;
696             }
697             if(this.sortInfo && this.remoteSort){
698                 var pn = this.paramNames;
699                 p[pn["sort"]] = this.sortInfo.field;
700                 p[pn["dir"]] = this.sortInfo.direction;
701             }
702             if (this.multiSort) {
703                 var pn = this.paramNames;
704                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
705             }
706             
707             this.proxy.load(p, this.reader, this.loadRecords, this, options);
708         }
709     },
710
711     /**
712      * Reloads the Record cache from the configured Proxy using the configured Reader and
713      * the options from the last load operation performed.
714      * @param {Object} options (optional) An object containing properties which may override the options
715      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
716      * the most recently used options are reused).
717      */
718     reload : function(options){
719         this.load(Roo.applyIf(options||{}, this.lastOptions));
720     },
721
722     // private
723     // Called as a callback by the Reader during a load operation.
724     loadRecords : function(o, options, success){
725          
726         if(!o){
727             if(success !== false){
728                 this.fireEvent("load", this, [], options, o);
729             }
730             if(options.callback){
731                 options.callback.call(options.scope || this, [], options, false);
732             }
733             return;
734         }
735         // if data returned failure - throw an exception.
736         if (o.success === false) {
737             // show a message if no listener is registered.
738             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
739                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
740             }
741             // loadmask wil be hooked into this..
742             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
743             return;
744         }
745         var r = o.records, t = o.totalRecords || r.length;
746         
747         this.fireEvent("beforeloadadd", this, r, options, o);
748         
749         if(!options || options.add !== true){
750             if(this.pruneModifiedRecords){
751                 this.modified = [];
752             }
753             for(var i = 0, len = r.length; i < len; i++){
754                 r[i].join(this);
755             }
756             if(this.snapshot){
757                 this.data = this.snapshot;
758                 delete this.snapshot;
759             }
760             this.data.clear();
761             this.data.addAll(r);
762             this.totalLength = t;
763             this.applySort();
764             this.fireEvent("datachanged", this);
765         }else{
766             this.totalLength = Math.max(t, this.data.length+r.length);
767             this.add(r);
768         }
769         
770         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
771                 
772             var e = new Roo.data.Record({});
773
774             e.set(this.parent.displayField, this.parent.emptyTitle);
775             e.set(this.parent.valueField, '');
776
777             this.insert(0, e);
778         }
779             
780         this.fireEvent("load", this, r, options, o);
781         if(options.callback){
782             options.callback.call(options.scope || this, r, options, true);
783         }
784     },
785
786
787     /**
788      * Loads data from a passed data block. A Reader which understands the format of the data
789      * must have been configured in the constructor.
790      * @param {Object} data The data block from which to read the Records.  The format of the data expected
791      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
792      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
793      */
794     loadData : function(o, append){
795         var r = this.reader.readRecords(o);
796         this.loadRecords(r, {add: append}, true);
797     },
798     
799      /**
800      * using 'cn' the nested child reader read the child array into it's child stores.
801      * @param {Object} rec The record with a 'children array
802      */
803     loadDataFromChildren : function(rec)
804     {
805         this.loadData(this.reader.toLoadData(rec));
806     },
807     
808
809     /**
810      * Gets the number of cached records.
811      * <p>
812      * <em>If using paging, this may not be the total size of the dataset. If the data object
813      * used by the Reader contains the dataset size, then the getTotalCount() function returns
814      * the data set size</em>
815      */
816     getCount : function(){
817         return this.data.length || 0;
818     },
819
820     /**
821      * Gets the total number of records in the dataset as returned by the server.
822      * <p>
823      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
824      * the dataset size</em>
825      */
826     getTotalCount : function(){
827         return this.totalLength || 0;
828     },
829
830     /**
831      * Returns the sort state of the Store as an object with two properties:
832      * <pre><code>
833  field {String} The name of the field by which the Records are sorted
834  direction {String} The sort order, "ASC" or "DESC"
835      * </code></pre>
836      */
837     getSortState : function(){
838         return this.sortInfo;
839     },
840
841     // private
842     applySort : function(){
843         if(this.sortInfo && !this.remoteSort){
844             var s = this.sortInfo, f = s.field;
845             var st = this.fields.get(f).sortType;
846             var fn = function(r1, r2){
847                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
848                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
849             };
850             this.data.sort(s.direction, fn);
851             if(this.snapshot && this.snapshot != this.data){
852                 this.snapshot.sort(s.direction, fn);
853             }
854         }
855     },
856
857     /**
858      * Sets the default sort column and order to be used by the next load operation.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     setDefaultSort : function(field, dir){
863         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
864     },
865
866     /**
867      * Sort the Records.
868      * If remote sorting is used, the sort is performed on the server, and the cache is
869      * reloaded. If local sorting is used, the cache is sorted internally.
870      * @param {String} fieldName The name of the field to sort by.
871      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
872      */
873     sort : function(fieldName, dir){
874         var f = this.fields.get(fieldName);
875         if(!dir){
876             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
877             
878             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
879                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
880             }else{
881                 dir = f.sortDir;
882             }
883         }
884         this.sortToggle[f.name] = dir;
885         this.sortInfo = {field: f.name, direction: dir};
886         if(!this.remoteSort){
887             this.applySort();
888             this.fireEvent("datachanged", this);
889         }else{
890             this.load(this.lastOptions);
891         }
892     },
893
894     /**
895      * Calls the specified function for each of the Records in the cache.
896      * @param {Function} fn The function to call. The Record is passed as the first parameter.
897      * Returning <em>false</em> aborts and exits the iteration.
898      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
899      */
900     each : function(fn, scope){
901         this.data.each(fn, scope);
902     },
903
904     /**
905      * Gets all records modified since the last commit.  Modified records are persisted across load operations
906      * (e.g., during paging).
907      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
908      */
909     getModifiedRecords : function(){
910         return this.modified;
911     },
912
913     // private
914     createFilterFn : function(property, value, anyMatch){
915         if(!value.exec){ // not a regex
916             value = String(value);
917             if(value.length == 0){
918                 return false;
919             }
920             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
921         }
922         return function(r){
923             return value.test(r.data[property]);
924         };
925     },
926
927     /**
928      * Sums the value of <i>property</i> for each record between start and end and returns the result.
929      * @param {String} property A field on your records
930      * @param {Number} start The record index to start at (defaults to 0)
931      * @param {Number} end The last record index to include (defaults to length - 1)
932      * @return {Number} The sum
933      */
934     sum : function(property, start, end){
935         var rs = this.data.items, v = 0;
936         start = start || 0;
937         end = (end || end === 0) ? end : rs.length-1;
938
939         for(var i = start; i <= end; i++){
940             v += (rs[i].data[property] || 0);
941         }
942         return v;
943     },
944
945     /**
946      * Filter the records by a specified property.
947      * @param {String} field A field on your records
948      * @param {String/RegExp} value Either a string that the field
949      * should start with or a RegExp to test against the field
950      * @param {Boolean} anyMatch True to match any part not just the beginning
951      */
952     filter : function(property, value, anyMatch){
953         var fn = this.createFilterFn(property, value, anyMatch);
954         return fn ? this.filterBy(fn) : this.clearFilter();
955     },
956
957     /**
958      * Filter by a function. The specified function will be called with each
959      * record in this data source. If the function returns true the record is included,
960      * otherwise it is filtered.
961      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
962      * @param {Object} scope (optional) The scope of the function (defaults to this)
963      */
964     filterBy : function(fn, scope){
965         this.snapshot = this.snapshot || this.data;
966         this.data = this.queryBy(fn, scope||this);
967         this.fireEvent("datachanged", this);
968     },
969
970     /**
971      * Query the records by a specified property.
972      * @param {String} field A field on your records
973      * @param {String/RegExp} value Either a string that the field
974      * should start with or a RegExp to test against the field
975      * @param {Boolean} anyMatch True to match any part not just the beginning
976      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
977      */
978     query : function(property, value, anyMatch){
979         var fn = this.createFilterFn(property, value, anyMatch);
980         return fn ? this.queryBy(fn) : this.data.clone();
981     },
982
983     /**
984      * Query by a function. The specified function will be called with each
985      * record in this data source. If the function returns true the record is included
986      * in the results.
987      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
988      * @param {Object} scope (optional) The scope of the function (defaults to this)
989       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
990      **/
991     queryBy : function(fn, scope){
992         var data = this.snapshot || this.data;
993         return data.filterBy(fn, scope||this);
994     },
995
996     /**
997      * Collects unique values for a particular dataIndex from this store.
998      * @param {String} dataIndex The property to collect
999      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
1000      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
1001      * @return {Array} An array of the unique values
1002      **/
1003     collect : function(dataIndex, allowNull, bypassFilter){
1004         var d = (bypassFilter === true && this.snapshot) ?
1005                 this.snapshot.items : this.data.items;
1006         var v, sv, r = [], l = {};
1007         for(var i = 0, len = d.length; i < len; i++){
1008             v = d[i].data[dataIndex];
1009             sv = String(v);
1010             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1011                 l[sv] = true;
1012                 r[r.length] = v;
1013             }
1014         }
1015         return r;
1016     },
1017
1018     /**
1019      * Revert to a view of the Record cache with no filtering applied.
1020      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1021      */
1022     clearFilter : function(suppressEvent){
1023         if(this.snapshot && this.snapshot != this.data){
1024             this.data = this.snapshot;
1025             delete this.snapshot;
1026             if(suppressEvent !== true){
1027                 this.fireEvent("datachanged", this);
1028             }
1029         }
1030     },
1031
1032     // private
1033     afterEdit : function(record){
1034         if(this.modified.indexOf(record) == -1){
1035             this.modified.push(record);
1036         }
1037         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1038     },
1039     
1040     // private
1041     afterReject : function(record){
1042         this.modified.remove(record);
1043         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1044     },
1045
1046     // private
1047     afterCommit : function(record){
1048         this.modified.remove(record);
1049         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1050     },
1051
1052     /**
1053      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1054      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1055      */
1056     commitChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].commit();
1061         }
1062     },
1063
1064     /**
1065      * Cancel outstanding changes on all changed records.
1066      */
1067     rejectChanges : function(){
1068         var m = this.modified.slice(0);
1069         this.modified = [];
1070         for(var i = 0, len = m.length; i < len; i++){
1071             m[i].reject();
1072         }
1073     },
1074
1075     onMetaChange : function(meta, rtype, o){
1076         this.recordType = rtype;
1077         this.fields = rtype.prototype.fields;
1078         delete this.snapshot;
1079         this.sortInfo = meta.sortInfo || this.sortInfo;
1080         this.modified = [];
1081         this.fireEvent('metachange', this, this.reader.meta);
1082     },
1083     
1084     moveIndex : function(data, type)
1085     {
1086         var index = this.indexOf(data);
1087         
1088         var newIndex = index + type;
1089         
1090         this.remove(data);
1091         
1092         this.insert(newIndex, data);
1093         
1094     }
1095 });/*
1096  * Based on:
1097  * Ext JS Library 1.1.1
1098  * Copyright(c) 2006-2007, Ext JS, LLC.
1099  *
1100  * Originally Released Under LGPL - original licence link has changed is not relivant.
1101  *
1102  * Fork - LGPL
1103  * <script type="text/javascript">
1104  */
1105
1106 /**
1107  * @class Roo.data.SimpleStore
1108  * @extends Roo.data.Store
1109  * Small helper class to make creating Stores from Array data easier.
1110  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1111  * @cfg {Array} fields An array of field definition objects, or field name strings.
1112  * @cfg {Object} an existing reader (eg. copied from another store)
1113  * @cfg {Array} data The multi-dimensional array of data
1114  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1115  * @cfg {Roo.data.Reader} reader  [not-required] 
1116  * @constructor
1117  * @param {Object} config
1118  */
1119 Roo.data.SimpleStore = function(config)
1120 {
1121     Roo.data.SimpleStore.superclass.constructor.call(this, {
1122         isLocal : true,
1123         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1124                 id: config.id
1125             },
1126             Roo.data.Record.create(config.fields)
1127         ),
1128         proxy : new Roo.data.MemoryProxy(config.data)
1129     });
1130     this.load();
1131 };
1132 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1133  * Based on:
1134  * Ext JS Library 1.1.1
1135  * Copyright(c) 2006-2007, Ext JS, LLC.
1136  *
1137  * Originally Released Under LGPL - original licence link has changed is not relivant.
1138  *
1139  * Fork - LGPL
1140  * <script type="text/javascript">
1141  */
1142
1143 /**
1144 /**
1145  * @extends Roo.data.Store
1146  * @class Roo.data.JsonStore
1147  * Small helper class to make creating Stores for JSON data easier. <br/>
1148 <pre><code>
1149 var store = new Roo.data.JsonStore({
1150     url: 'get-images.php',
1151     root: 'images',
1152     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1153 });
1154 </code></pre>
1155  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1156  * JsonReader and HttpProxy (unless inline data is provided).</b>
1157  * @cfg {Array} fields An array of field definition objects, or field name strings.
1158  * @constructor
1159  * @param {Object} config
1160  */
1161 Roo.data.JsonStore = function(c){
1162     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1163         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1164         reader: new Roo.data.JsonReader(c, c.fields)
1165     }));
1166 };
1167 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1168  * Based on:
1169  * Ext JS Library 1.1.1
1170  * Copyright(c) 2006-2007, Ext JS, LLC.
1171  *
1172  * Originally Released Under LGPL - original licence link has changed is not relivant.
1173  *
1174  * Fork - LGPL
1175  * <script type="text/javascript">
1176  */
1177
1178  
1179 Roo.data.Field = function(config){
1180     if(typeof config == "string"){
1181         config = {name: config};
1182     }
1183     Roo.apply(this, config);
1184     
1185     if(!this.type){
1186         this.type = "auto";
1187     }
1188     
1189     var st = Roo.data.SortTypes;
1190     // named sortTypes are supported, here we look them up
1191     if(typeof this.sortType == "string"){
1192         this.sortType = st[this.sortType];
1193     }
1194     
1195     // set default sortType for strings and dates
1196     if(!this.sortType){
1197         switch(this.type){
1198             case "string":
1199                 this.sortType = st.asUCString;
1200                 break;
1201             case "date":
1202                 this.sortType = st.asDate;
1203                 break;
1204             default:
1205                 this.sortType = st.none;
1206         }
1207     }
1208
1209     // define once
1210     var stripRe = /[\$,%]/g;
1211
1212     // prebuilt conversion function for this field, instead of
1213     // switching every time we're reading a value
1214     if(!this.convert){
1215         var cv, dateFormat = this.dateFormat;
1216         switch(this.type){
1217             case "":
1218             case "auto":
1219             case undefined:
1220                 cv = function(v){ return v; };
1221                 break;
1222             case "string":
1223                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1224                 break;
1225             case "int":
1226                 cv = function(v){
1227                     return v !== undefined && v !== null && v !== '' ?
1228                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1229                     };
1230                 break;
1231             case "float":
1232                 cv = function(v){
1233                     return v !== undefined && v !== null && v !== '' ?
1234                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1235                     };
1236                 break;
1237             case "bool":
1238             case "boolean":
1239                 cv = function(v){ return v === true || v === "true" || v == 1; };
1240                 break;
1241             case "date":
1242                 cv = function(v){
1243                     if(!v){
1244                         return '';
1245                     }
1246                     if(v instanceof Date){
1247                         return v;
1248                     }
1249                     if(dateFormat){
1250                         if(dateFormat == "timestamp"){
1251                             return new Date(v*1000);
1252                         }
1253                         return Date.parseDate(v, dateFormat);
1254                     }
1255                     var parsed = Date.parse(v);
1256                     return parsed ? new Date(parsed) : null;
1257                 };
1258              break;
1259             
1260         }
1261         this.convert = cv;
1262     }
1263 };
1264
1265 Roo.data.Field.prototype = {
1266     dateFormat: null,
1267     defaultValue: "",
1268     mapping: null,
1269     sortType : null,
1270     sortDir : "ASC"
1271 };/*
1272  * Based on:
1273  * Ext JS Library 1.1.1
1274  * Copyright(c) 2006-2007, Ext JS, LLC.
1275  *
1276  * Originally Released Under LGPL - original licence link has changed is not relivant.
1277  *
1278  * Fork - LGPL
1279  * <script type="text/javascript">
1280  */
1281  
1282 // Base class for reading structured data from a data source.  This class is intended to be
1283 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1284
1285 /**
1286  * @class Roo.data.DataReader
1287  * @abstract
1288  * Base class for reading structured data from a data source.  This class is intended to be
1289  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1290  */
1291
1292 Roo.data.DataReader = function(meta, recordType){
1293     
1294     this.meta = meta;
1295     
1296     this.recordType = recordType instanceof Array ? 
1297         Roo.data.Record.create(recordType) : recordType;
1298 };
1299
1300 Roo.data.DataReader.prototype = {
1301     
1302     
1303     readerType : 'Data',
1304      /**
1305      * Create an empty record
1306      * @param {Object} data (optional) - overlay some values
1307      * @return {Roo.data.Record} record created.
1308      */
1309     newRow :  function(d) {
1310         var da =  {};
1311         this.recordType.prototype.fields.each(function(c) {
1312             switch( c.type) {
1313                 case 'int' : da[c.name] = 0; break;
1314                 case 'date' : da[c.name] = new Date(); break;
1315                 case 'float' : da[c.name] = 0.0; break;
1316                 case 'boolean' : da[c.name] = false; break;
1317                 default : da[c.name] = ""; break;
1318             }
1319             
1320         });
1321         return new this.recordType(Roo.apply(da, d));
1322     }
1323     
1324     
1325 };/*
1326  * Based on:
1327  * Ext JS Library 1.1.1
1328  * Copyright(c) 2006-2007, Ext JS, LLC.
1329  *
1330  * Originally Released Under LGPL - original licence link has changed is not relivant.
1331  *
1332  * Fork - LGPL
1333  * <script type="text/javascript">
1334  */
1335
1336 /**
1337  * @class Roo.data.DataProxy
1338  * @extends Roo.util.Observable
1339  * @abstract
1340  * This class is an abstract base class for implementations which provide retrieval of
1341  * unformatted data objects.<br>
1342  * <p>
1343  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1344  * (of the appropriate type which knows how to parse the data object) to provide a block of
1345  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1346  * <p>
1347  * Custom implementations must implement the load method as described in
1348  * {@link Roo.data.HttpProxy#load}.
1349  */
1350 Roo.data.DataProxy = function(){
1351     this.addEvents({
1352         /**
1353          * @event beforeload
1354          * Fires before a network request is made to retrieve a data object.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} params The params parameter to the load function.
1357          */
1358         beforeload : true,
1359         /**
1360          * @event load
1361          * Fires before the load method's callback is called.
1362          * @param {Object} This DataProxy object.
1363          * @param {Object} o The data object.
1364          * @param {Object} arg The callback argument object passed to the load function.
1365          */
1366         load : true,
1367         /**
1368          * @event loadexception
1369          * Fires if an Exception occurs during data retrieval.
1370          * @param {Object} This DataProxy object.
1371          * @param {Object} o The data object.
1372          * @param {Object} arg The callback argument object passed to the load function.
1373          * @param {Object} e The Exception.
1374          */
1375         loadexception : true
1376     });
1377     Roo.data.DataProxy.superclass.constructor.call(this);
1378 };
1379
1380 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1381
1382     /**
1383      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1384      */
1385 /*
1386  * Based on:
1387  * Ext JS Library 1.1.1
1388  * Copyright(c) 2006-2007, Ext JS, LLC.
1389  *
1390  * Originally Released Under LGPL - original licence link has changed is not relivant.
1391  *
1392  * Fork - LGPL
1393  * <script type="text/javascript">
1394  */
1395 /**
1396  * @class Roo.data.MemoryProxy
1397  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1398  * to the Reader when its load method is called.
1399  * @constructor
1400  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1401  */
1402 Roo.data.MemoryProxy = function(data){
1403     if (data.data) {
1404         data = data.data;
1405     }
1406     Roo.data.MemoryProxy.superclass.constructor.call(this);
1407     this.data = data;
1408 };
1409
1410 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1411     
1412     /**
1413      * Load data from the requested source (in this case an in-memory
1414      * data object passed to the constructor), read the data object into
1415      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1416      * process that block using the passed callback.
1417      * @param {Object} params This parameter is not used by the MemoryProxy class.
1418      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1419      * object into a block of Roo.data.Records.
1420      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1421      * The function must be passed <ul>
1422      * <li>The Record block object</li>
1423      * <li>The "arg" argument from the load function</li>
1424      * <li>A boolean success indicator</li>
1425      * </ul>
1426      * @param {Object} scope The scope in which to call the callback
1427      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1428      */
1429     load : function(params, reader, callback, scope, arg){
1430         params = params || {};
1431         var result;
1432         try {
1433             result = reader.readRecords(params.data ? params.data :this.data);
1434         }catch(e){
1435             this.fireEvent("loadexception", this, arg, null, e);
1436             callback.call(scope, null, arg, false);
1437             return;
1438         }
1439         callback.call(scope, result, arg, true);
1440     },
1441     
1442     // private
1443     update : function(params, records){
1444         
1445     }
1446 });/*
1447  * Based on:
1448  * Ext JS Library 1.1.1
1449  * Copyright(c) 2006-2007, Ext JS, LLC.
1450  *
1451  * Originally Released Under LGPL - original licence link has changed is not relivant.
1452  *
1453  * Fork - LGPL
1454  * <script type="text/javascript">
1455  */
1456 /**
1457  * @class Roo.data.HttpProxy
1458  * @extends Roo.data.DataProxy
1459  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1460  * configured to reference a certain URL.<br><br>
1461  * <p>
1462  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1463  * from which the running page was served.<br><br>
1464  * <p>
1465  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1466  * <p>
1467  * Be aware that to enable the browser to parse an XML document, the server must set
1468  * the Content-Type header in the HTTP response to "text/xml".
1469  * @constructor
1470  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1471  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1472  * will be used to make the request.
1473  */
1474 Roo.data.HttpProxy = function(conn){
1475     Roo.data.HttpProxy.superclass.constructor.call(this);
1476     // is conn a conn config or a real conn?
1477     this.conn = conn;
1478     this.useAjax = !conn || !conn.events;
1479   
1480 };
1481
1482 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1483     // thse are take from connection...
1484     
1485     /**
1486      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1487      */
1488     /**
1489      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1490      * extra parameters to each request made by this object. (defaults to undefined)
1491      */
1492     /**
1493      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1494      *  to each request made by this object. (defaults to undefined)
1495      */
1496     /**
1497      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1498      */
1499     /**
1500      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1501      */
1502      /**
1503      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1504      * @type Boolean
1505      */
1506   
1507
1508     /**
1509      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1510      * @type Boolean
1511      */
1512     /**
1513      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1514      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1515      * a finer-grained basis than the DataProxy events.
1516      */
1517     getConnection : function(){
1518         return this.useAjax ? Roo.Ajax : this.conn;
1519     },
1520
1521     /**
1522      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1523      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1524      * process that block using the passed callback.
1525      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1526      * for the request to the remote server.
1527      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1528      * object into a block of Roo.data.Records.
1529      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1530      * The function must be passed <ul>
1531      * <li>The Record block object</li>
1532      * <li>The "arg" argument from the load function</li>
1533      * <li>A boolean success indicator</li>
1534      * </ul>
1535      * @param {Object} scope The scope in which to call the callback
1536      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1537      */
1538     load : function(params, reader, callback, scope, arg){
1539         if(this.fireEvent("beforeload", this, params) !== false){
1540             var  o = {
1541                 params : params || {},
1542                 request: {
1543                     callback : callback,
1544                     scope : scope,
1545                     arg : arg
1546                 },
1547                 reader: reader,
1548                 callback : this.loadResponse,
1549                 scope: this
1550             };
1551             if(this.useAjax){
1552                 Roo.applyIf(o, this.conn);
1553                 if(this.activeRequest){
1554                     Roo.Ajax.abort(this.activeRequest);
1555                 }
1556                 this.activeRequest = Roo.Ajax.request(o);
1557             }else{
1558                 this.conn.request(o);
1559             }
1560         }else{
1561             callback.call(scope||this, null, arg, false);
1562         }
1563     },
1564
1565     // private
1566     loadResponse : function(o, success, response){
1567         delete this.activeRequest;
1568         if(!success){
1569             this.fireEvent("loadexception", this, o, response);
1570             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1571             return;
1572         }
1573         var result;
1574         try {
1575             result = o.reader.read(response);
1576         }catch(e){
1577             o.success = false;
1578             o.raw = { errorMsg : response.responseText };
1579             this.fireEvent("loadexception", this, o, response, e);
1580             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1581             return;
1582         }
1583         
1584         this.fireEvent("load", this, o, o.request.arg);
1585         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1586     },
1587
1588     // private
1589     update : function(dataSet){
1590
1591     },
1592
1593     // private
1594     updateResponse : function(dataSet){
1595
1596     }
1597 });/*
1598  * Based on:
1599  * Ext JS Library 1.1.1
1600  * Copyright(c) 2006-2007, Ext JS, LLC.
1601  *
1602  * Originally Released Under LGPL - original licence link has changed is not relivant.
1603  *
1604  * Fork - LGPL
1605  * <script type="text/javascript">
1606  */
1607
1608 /**
1609  * @class Roo.data.ScriptTagProxy
1610  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1611  * other than the originating domain of the running page.<br><br>
1612  * <p>
1613  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1614  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1615  * <p>
1616  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1617  * source code that is used as the source inside a &lt;script> tag.<br><br>
1618  * <p>
1619  * In order for the browser to process the returned data, the server must wrap the data object
1620  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1621  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1622  * depending on whether the callback name was passed:
1623  * <p>
1624  * <pre><code>
1625 boolean scriptTag = false;
1626 String cb = request.getParameter("callback");
1627 if (cb != null) {
1628     scriptTag = true;
1629     response.setContentType("text/javascript");
1630 } else {
1631     response.setContentType("application/x-json");
1632 }
1633 Writer out = response.getWriter();
1634 if (scriptTag) {
1635     out.write(cb + "(");
1636 }
1637 out.print(dataBlock.toJsonString());
1638 if (scriptTag) {
1639     out.write(");");
1640 }
1641 </pre></code>
1642  *
1643  * @constructor
1644  * @param {Object} config A configuration object.
1645  */
1646 Roo.data.ScriptTagProxy = function(config){
1647     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1648     Roo.apply(this, config);
1649     this.head = document.getElementsByTagName("head")[0];
1650 };
1651
1652 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1653
1654 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1655     /**
1656      * @cfg {String} url The URL from which to request the data object.
1657      */
1658     /**
1659      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1660      */
1661     timeout : 30000,
1662     /**
1663      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1664      * the server the name of the callback function set up by the load call to process the returned data object.
1665      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1666      * javascript output which calls this named function passing the data object as its only parameter.
1667      */
1668     callbackParam : "callback",
1669     /**
1670      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1671      * name to the request.
1672      */
1673     nocache : true,
1674
1675     /**
1676      * Load data from the configured URL, read the data object into
1677      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1678      * process that block using the passed callback.
1679      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1680      * for the request to the remote server.
1681      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1682      * object into a block of Roo.data.Records.
1683      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1684      * The function must be passed <ul>
1685      * <li>The Record block object</li>
1686      * <li>The "arg" argument from the load function</li>
1687      * <li>A boolean success indicator</li>
1688      * </ul>
1689      * @param {Object} scope The scope in which to call the callback
1690      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1691      */
1692     load : function(params, reader, callback, scope, arg){
1693         if(this.fireEvent("beforeload", this, params) !== false){
1694
1695             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1696
1697             var url = this.url;
1698             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1699             if(this.nocache){
1700                 url += "&_dc=" + (new Date().getTime());
1701             }
1702             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1703             var trans = {
1704                 id : transId,
1705                 cb : "stcCallback"+transId,
1706                 scriptId : "stcScript"+transId,
1707                 params : params,
1708                 arg : arg,
1709                 url : url,
1710                 callback : callback,
1711                 scope : scope,
1712                 reader : reader
1713             };
1714             var conn = this;
1715
1716             window[trans.cb] = function(o){
1717                 conn.handleResponse(o, trans);
1718             };
1719
1720             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1721
1722             if(this.autoAbort !== false){
1723                 this.abort();
1724             }
1725
1726             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1727
1728             var script = document.createElement("script");
1729             script.setAttribute("src", url);
1730             script.setAttribute("type", "text/javascript");
1731             script.setAttribute("id", trans.scriptId);
1732             this.head.appendChild(script);
1733
1734             this.trans = trans;
1735         }else{
1736             callback.call(scope||this, null, arg, false);
1737         }
1738     },
1739
1740     // private
1741     isLoading : function(){
1742         return this.trans ? true : false;
1743     },
1744
1745     /**
1746      * Abort the current server request.
1747      */
1748     abort : function(){
1749         if(this.isLoading()){
1750             this.destroyTrans(this.trans);
1751         }
1752     },
1753
1754     // private
1755     destroyTrans : function(trans, isLoaded){
1756         this.head.removeChild(document.getElementById(trans.scriptId));
1757         clearTimeout(trans.timeoutId);
1758         if(isLoaded){
1759             window[trans.cb] = undefined;
1760             try{
1761                 delete window[trans.cb];
1762             }catch(e){}
1763         }else{
1764             // if hasn't been loaded, wait for load to remove it to prevent script error
1765             window[trans.cb] = function(){
1766                 window[trans.cb] = undefined;
1767                 try{
1768                     delete window[trans.cb];
1769                 }catch(e){}
1770             };
1771         }
1772     },
1773
1774     // private
1775     handleResponse : function(o, trans){
1776         this.trans = false;
1777         this.destroyTrans(trans, true);
1778         var result;
1779         try {
1780             result = trans.reader.readRecords(o);
1781         }catch(e){
1782             this.fireEvent("loadexception", this, o, trans.arg, e);
1783             trans.callback.call(trans.scope||window, null, trans.arg, false);
1784             return;
1785         }
1786         this.fireEvent("load", this, o, trans.arg);
1787         trans.callback.call(trans.scope||window, result, trans.arg, true);
1788     },
1789
1790     // private
1791     handleFailure : function(trans){
1792         this.trans = false;
1793         this.destroyTrans(trans, false);
1794         this.fireEvent("loadexception", this, null, trans.arg);
1795         trans.callback.call(trans.scope||window, null, trans.arg, false);
1796     }
1797 });/*
1798  * Based on:
1799  * Ext JS Library 1.1.1
1800  * Copyright(c) 2006-2007, Ext JS, LLC.
1801  *
1802  * Originally Released Under LGPL - original licence link has changed is not relivant.
1803  *
1804  * Fork - LGPL
1805  * <script type="text/javascript">
1806  */
1807
1808 /**
1809  * @class Roo.data.JsonReader
1810  * @extends Roo.data.DataReader
1811  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1812  * based on mappings in a provided Roo.data.Record constructor.
1813  * 
1814  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1815  * in the reply previously. 
1816  * 
1817  * <p>
1818  * Example code:
1819  * <pre><code>
1820 var RecordDef = Roo.data.Record.create([
1821     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1822     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1823 ]);
1824 var myReader = new Roo.data.JsonReader({
1825     totalProperty: "results",    // The property which contains the total dataset size (optional)
1826     root: "rows",                // The property which contains an Array of row objects
1827     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1828 }, RecordDef);
1829 </code></pre>
1830  * <p>
1831  * This would consume a JSON file like this:
1832  * <pre><code>
1833 { 'results': 2, 'rows': [
1834     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1835     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1836 }
1837 </code></pre>
1838  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1839  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1840  * paged from the remote server.
1841  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1842  * @cfg {String} root name of the property which contains the Array of row objects.
1843  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1844  * @cfg {Array} fields Array of field definition objects
1845  * @constructor
1846  * Create a new JsonReader
1847  * @param {Object} meta Metadata configuration options
1848  * @param {Object} recordType Either an Array of field definition objects,
1849  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1850  */
1851 Roo.data.JsonReader = function(meta, recordType){
1852     
1853     meta = meta || {};
1854     // set some defaults:
1855     Roo.applyIf(meta, {
1856         totalProperty: 'total',
1857         successProperty : 'success',
1858         root : 'data',
1859         id : 'id'
1860     });
1861     
1862     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1863 };
1864 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1865     
1866     readerType : 'Json',
1867     
1868     /**
1869      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1870      * Used by Store query builder to append _requestMeta to params.
1871      * 
1872      */
1873     metaFromRemote : false,
1874     /**
1875      * This method is only used by a DataProxy which has retrieved data from a remote server.
1876      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1877      * @return {Object} data A data block which is used by an Roo.data.Store object as
1878      * a cache of Roo.data.Records.
1879      */
1880     read : function(response){
1881         var json = response.responseText;
1882        
1883         var o = /* eval:var:o */ eval("("+json+")");
1884         if(!o) {
1885             throw {message: "JsonReader.read: Json object not found"};
1886         }
1887         
1888         if(o.metaData){
1889             
1890             delete this.ef;
1891             this.metaFromRemote = true;
1892             this.meta = o.metaData;
1893             this.recordType = Roo.data.Record.create(o.metaData.fields);
1894             this.onMetaChange(this.meta, this.recordType, o);
1895         }
1896         return this.readRecords(o);
1897     },
1898
1899     // private function a store will implement
1900     onMetaChange : function(meta, recordType, o){
1901
1902     },
1903
1904     /**
1905          * @ignore
1906          */
1907     simpleAccess: function(obj, subsc) {
1908         return obj[subsc];
1909     },
1910
1911         /**
1912          * @ignore
1913          */
1914     getJsonAccessor: function(){
1915         var re = /[\[\.]/;
1916         return function(expr) {
1917             try {
1918                 return(re.test(expr))
1919                     ? new Function("obj", "return obj." + expr)
1920                     : function(obj){
1921                         return obj[expr];
1922                     };
1923             } catch(e){}
1924             return Roo.emptyFn;
1925         };
1926     }(),
1927
1928     /**
1929      * Create a data block containing Roo.data.Records from an XML document.
1930      * @param {Object} o An object which contains an Array of row objects in the property specified
1931      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1932      * which contains the total size of the dataset.
1933      * @return {Object} data A data block which is used by an Roo.data.Store object as
1934      * a cache of Roo.data.Records.
1935      */
1936     readRecords : function(o){
1937         /**
1938          * After any data loads, the raw JSON data is available for further custom processing.
1939          * @type Object
1940          */
1941         this.o = o;
1942         var s = this.meta, Record = this.recordType,
1943             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1944
1945 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1946         if (!this.ef) {
1947             if(s.totalProperty) {
1948                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1949                 }
1950                 if(s.successProperty) {
1951                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1952                 }
1953                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1954                 if (s.id) {
1955                         var g = this.getJsonAccessor(s.id);
1956                         this.getId = function(rec) {
1957                                 var r = g(rec);  
1958                                 return (r === undefined || r === "") ? null : r;
1959                         };
1960                 } else {
1961                         this.getId = function(){return null;};
1962                 }
1963             this.ef = [];
1964             for(var jj = 0; jj < fl; jj++){
1965                 f = fi[jj];
1966                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1967                 this.ef[jj] = this.getJsonAccessor(map);
1968             }
1969         }
1970
1971         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1972         if(s.totalProperty){
1973             var vt = parseInt(this.getTotal(o), 10);
1974             if(!isNaN(vt)){
1975                 totalRecords = vt;
1976             }
1977         }
1978         if(s.successProperty){
1979             var vs = this.getSuccess(o);
1980             if(vs === false || vs === 'false'){
1981                 success = false;
1982             }
1983         }
1984         var records = [];
1985         for(var i = 0; i < c; i++){
1986             var n = root[i];
1987             var values = {};
1988             var id = this.getId(n);
1989             for(var j = 0; j < fl; j++){
1990                 f = fi[j];
1991                                 var v = this.ef[j](n);
1992                                 if (!f.convert) {
1993                                         Roo.log('missing convert for ' + f.name);
1994                                         Roo.log(f);
1995                                         continue;
1996                                 }
1997                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1998             }
1999                         if (!Record) {
2000                                 return {
2001                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2002                                         success : false,
2003                                         records : [],
2004                                         totalRecords : 0
2005                                 };
2006                         }
2007             var record = new Record(values, id);
2008             record.json = n;
2009             records[i] = record;
2010         }
2011         return {
2012             raw : o,
2013             success : success,
2014             records : records,
2015             totalRecords : totalRecords
2016         };
2017     },
2018     // used when loading children.. @see loadDataFromChildren
2019     toLoadData: function(rec)
2020     {
2021         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2022         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2023         return { data : data, total : data.length };
2024         
2025     }
2026 });/*
2027  * Based on:
2028  * Ext JS Library 1.1.1
2029  * Copyright(c) 2006-2007, Ext JS, LLC.
2030  *
2031  * Originally Released Under LGPL - original licence link has changed is not relivant.
2032  *
2033  * Fork - LGPL
2034  * <script type="text/javascript">
2035  */
2036
2037 /**
2038  * @class Roo.data.XmlReader
2039  * @extends Roo.data.DataReader
2040  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2041  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2042  * <p>
2043  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2044  * header in the HTTP response must be set to "text/xml".</em>
2045  * <p>
2046  * Example code:
2047  * <pre><code>
2048 var RecordDef = Roo.data.Record.create([
2049    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2050    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2051 ]);
2052 var myReader = new Roo.data.XmlReader({
2053    totalRecords: "results", // The element which contains the total dataset size (optional)
2054    record: "row",           // The repeated element which contains row information
2055    id: "id"                 // The element within the row that provides an ID for the record (optional)
2056 }, RecordDef);
2057 </code></pre>
2058  * <p>
2059  * This would consume an XML file like this:
2060  * <pre><code>
2061 &lt;?xml?>
2062 &lt;dataset>
2063  &lt;results>2&lt;/results>
2064  &lt;row>
2065    &lt;id>1&lt;/id>
2066    &lt;name>Bill&lt;/name>
2067    &lt;occupation>Gardener&lt;/occupation>
2068  &lt;/row>
2069  &lt;row>
2070    &lt;id>2&lt;/id>
2071    &lt;name>Ben&lt;/name>
2072    &lt;occupation>Horticulturalist&lt;/occupation>
2073  &lt;/row>
2074 &lt;/dataset>
2075 </code></pre>
2076  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2077  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2078  * paged from the remote server.
2079  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2080  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2081  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2082  * a record identifier value.
2083  * @constructor
2084  * Create a new XmlReader
2085  * @param {Object} meta Metadata configuration options
2086  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2087  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2088  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2089  */
2090 Roo.data.XmlReader = function(meta, recordType){
2091     meta = meta || {};
2092     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2093 };
2094 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2095     
2096     readerType : 'Xml',
2097     
2098     /**
2099      * This method is only used by a DataProxy which has retrieved data from a remote server.
2100          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2101          * to contain a method called 'responseXML' that returns an XML document object.
2102      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2103      * a cache of Roo.data.Records.
2104      */
2105     read : function(response){
2106         var doc = response.responseXML;
2107         if(!doc) {
2108             throw {message: "XmlReader.read: XML Document not available"};
2109         }
2110         return this.readRecords(doc);
2111     },
2112
2113     /**
2114      * Create a data block containing Roo.data.Records from an XML document.
2115          * @param {Object} doc A parsed XML document.
2116      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2117      * a cache of Roo.data.Records.
2118      */
2119     readRecords : function(doc){
2120         /**
2121          * After any data loads/reads, the raw XML Document is available for further custom processing.
2122          * @type XMLDocument
2123          */
2124         this.xmlData = doc;
2125         var root = doc.documentElement || doc;
2126         var q = Roo.DomQuery;
2127         var recordType = this.recordType, fields = recordType.prototype.fields;
2128         var sid = this.meta.id;
2129         var totalRecords = 0, success = true;
2130         if(this.meta.totalRecords){
2131             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2132         }
2133         
2134         if(this.meta.success){
2135             var sv = q.selectValue(this.meta.success, root, true);
2136             success = sv !== false && sv !== 'false';
2137         }
2138         var records = [];
2139         var ns = q.select(this.meta.record, root);
2140         for(var i = 0, len = ns.length; i < len; i++) {
2141                 var n = ns[i];
2142                 var values = {};
2143                 var id = sid ? q.selectValue(sid, n) : undefined;
2144                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2145                     var f = fields.items[j];
2146                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2147                     v = f.convert(v);
2148                     values[f.name] = v;
2149                 }
2150                 var record = new recordType(values, id);
2151                 record.node = n;
2152                 records[records.length] = record;
2153             }
2154
2155             return {
2156                 success : success,
2157                 records : records,
2158                 totalRecords : totalRecords || records.length
2159             };
2160     }
2161 });/*
2162  * Based on:
2163  * Ext JS Library 1.1.1
2164  * Copyright(c) 2006-2007, Ext JS, LLC.
2165  *
2166  * Originally Released Under LGPL - original licence link has changed is not relivant.
2167  *
2168  * Fork - LGPL
2169  * <script type="text/javascript">
2170  */
2171
2172 /**
2173  * @class Roo.data.ArrayReader
2174  * @extends Roo.data.DataReader
2175  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2176  * Each element of that Array represents a row of data fields. The
2177  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2178  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2179  * <p>
2180  * Example code:.
2181  * <pre><code>
2182 var RecordDef = Roo.data.Record.create([
2183     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2184     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2185 ]);
2186 var myReader = new Roo.data.ArrayReader({
2187     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2188 }, RecordDef);
2189 </code></pre>
2190  * <p>
2191  * This would consume an Array like this:
2192  * <pre><code>
2193 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2194   </code></pre>
2195  
2196  * @constructor
2197  * Create a new JsonReader
2198  * @param {Object} meta Metadata configuration options.
2199  * @param {Object|Array} recordType Either an Array of field definition objects
2200  * 
2201  * @cfg {Array} fields Array of field definition objects
2202  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2203  * as specified to {@link Roo.data.Record#create},
2204  * or an {@link Roo.data.Record} object
2205  *
2206  * 
2207  * created using {@link Roo.data.Record#create}.
2208  */
2209 Roo.data.ArrayReader = function(meta, recordType)
2210 {    
2211     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2212 };
2213
2214 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2215     
2216       /**
2217      * Create a data block containing Roo.data.Records from an XML document.
2218      * @param {Object} o An Array of row objects which represents the dataset.
2219      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2220      * a cache of Roo.data.Records.
2221      */
2222     readRecords : function(o)
2223     {
2224         var sid = this.meta ? this.meta.id : null;
2225         var recordType = this.recordType, fields = recordType.prototype.fields;
2226         var records = [];
2227         var root = o;
2228         for(var i = 0; i < root.length; i++){
2229             var n = root[i];
2230             var values = {};
2231             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2232             for(var j = 0, jlen = fields.length; j < jlen; j++){
2233                 var f = fields.items[j];
2234                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2235                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2236                 v = f.convert(v);
2237                 values[f.name] = v;
2238             }
2239             var record = new recordType(values, id);
2240             record.json = n;
2241             records[records.length] = record;
2242         }
2243         return {
2244             records : records,
2245             totalRecords : records.length
2246         };
2247     },
2248     // used when loading children.. @see loadDataFromChildren
2249     toLoadData: function(rec)
2250     {
2251         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2252         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2253         
2254     }
2255     
2256     
2257 });/*
2258  * Based on:
2259  * Ext JS Library 1.1.1
2260  * Copyright(c) 2006-2007, Ext JS, LLC.
2261  *
2262  * Originally Released Under LGPL - original licence link has changed is not relivant.
2263  *
2264  * Fork - LGPL
2265  * <script type="text/javascript">
2266  */
2267
2268
2269 /**
2270  * @class Roo.data.Tree
2271  * @extends Roo.util.Observable
2272  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2273  * in the tree have most standard DOM functionality.
2274  * @constructor
2275  * @param {Node} root (optional) The root node
2276  */
2277 Roo.data.Tree = function(root){
2278    this.nodeHash = {};
2279    /**
2280     * The root node for this tree
2281     * @type Node
2282     */
2283    this.root = null;
2284    if(root){
2285        this.setRootNode(root);
2286    }
2287    this.addEvents({
2288        /**
2289         * @event append
2290         * Fires when a new child node is appended to a node in this tree.
2291         * @param {Tree} tree The owner tree
2292         * @param {Node} parent The parent node
2293         * @param {Node} node The newly appended node
2294         * @param {Number} index The index of the newly appended node
2295         */
2296        "append" : true,
2297        /**
2298         * @event remove
2299         * Fires when a child node is removed from a node in this tree.
2300         * @param {Tree} tree The owner tree
2301         * @param {Node} parent The parent node
2302         * @param {Node} node The child node removed
2303         */
2304        "remove" : true,
2305        /**
2306         * @event move
2307         * Fires when a node is moved to a new location in the tree
2308         * @param {Tree} tree The owner tree
2309         * @param {Node} node The node moved
2310         * @param {Node} oldParent The old parent of this node
2311         * @param {Node} newParent The new parent of this node
2312         * @param {Number} index The index it was moved to
2313         */
2314        "move" : true,
2315        /**
2316         * @event insert
2317         * Fires when a new child node is inserted in a node in this tree.
2318         * @param {Tree} tree The owner tree
2319         * @param {Node} parent The parent node
2320         * @param {Node} node The child node inserted
2321         * @param {Node} refNode The child node the node was inserted before
2322         */
2323        "insert" : true,
2324        /**
2325         * @event beforeappend
2326         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2327         * @param {Tree} tree The owner tree
2328         * @param {Node} parent The parent node
2329         * @param {Node} node The child node to be appended
2330         */
2331        "beforeappend" : true,
2332        /**
2333         * @event beforeremove
2334         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2335         * @param {Tree} tree The owner tree
2336         * @param {Node} parent The parent node
2337         * @param {Node} node The child node to be removed
2338         */
2339        "beforeremove" : true,
2340        /**
2341         * @event beforemove
2342         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2343         * @param {Tree} tree The owner tree
2344         * @param {Node} node The node being moved
2345         * @param {Node} oldParent The parent of the node
2346         * @param {Node} newParent The new parent the node is moving to
2347         * @param {Number} index The index it is being moved to
2348         */
2349        "beforemove" : true,
2350        /**
2351         * @event beforeinsert
2352         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2353         * @param {Tree} tree The owner tree
2354         * @param {Node} parent The parent node
2355         * @param {Node} node The child node to be inserted
2356         * @param {Node} refNode The child node the node is being inserted before
2357         */
2358        "beforeinsert" : true
2359    });
2360
2361     Roo.data.Tree.superclass.constructor.call(this);
2362 };
2363
2364 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2365     pathSeparator: "/",
2366
2367     proxyNodeEvent : function(){
2368         return this.fireEvent.apply(this, arguments);
2369     },
2370
2371     /**
2372      * Returns the root node for this tree.
2373      * @return {Node}
2374      */
2375     getRootNode : function(){
2376         return this.root;
2377     },
2378
2379     /**
2380      * Sets the root node for this tree.
2381      * @param {Node} node
2382      * @return {Node}
2383      */
2384     setRootNode : function(node){
2385         this.root = node;
2386         node.ownerTree = this;
2387         node.isRoot = true;
2388         this.registerNode(node);
2389         return node;
2390     },
2391
2392     /**
2393      * Gets a node in this tree by its id.
2394      * @param {String} id
2395      * @return {Node}
2396      */
2397     getNodeById : function(id){
2398         return this.nodeHash[id];
2399     },
2400
2401     registerNode : function(node){
2402         this.nodeHash[node.id] = node;
2403     },
2404
2405     unregisterNode : function(node){
2406         delete this.nodeHash[node.id];
2407     },
2408
2409     toString : function(){
2410         return "[Tree"+(this.id?" "+this.id:"")+"]";
2411     }
2412 });
2413
2414 /**
2415  * @class Roo.data.Node
2416  * @extends Roo.util.Observable
2417  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2418  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2419  * @constructor
2420  * @param {Object} attributes The attributes/config for the node
2421  */
2422 Roo.data.Node = function(attributes){
2423     /**
2424      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2425      * @type {Object}
2426      */
2427     this.attributes = attributes || {};
2428     this.leaf = this.attributes.leaf;
2429     /**
2430      * The node id. @type String
2431      */
2432     this.id = this.attributes.id;
2433     if(!this.id){
2434         this.id = Roo.id(null, "ynode-");
2435         this.attributes.id = this.id;
2436     }
2437      
2438     
2439     /**
2440      * All child nodes of this node. @type Array
2441      */
2442     this.childNodes = [];
2443     if(!this.childNodes.indexOf){ // indexOf is a must
2444         this.childNodes.indexOf = function(o){
2445             for(var i = 0, len = this.length; i < len; i++){
2446                 if(this[i] == o) {
2447                     return i;
2448                 }
2449             }
2450             return -1;
2451         };
2452     }
2453     /**
2454      * The parent node for this node. @type Node
2455      */
2456     this.parentNode = null;
2457     /**
2458      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2459      */
2460     this.firstChild = null;
2461     /**
2462      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2463      */
2464     this.lastChild = null;
2465     /**
2466      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2467      */
2468     this.previousSibling = null;
2469     /**
2470      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2471      */
2472     this.nextSibling = null;
2473
2474     this.addEvents({
2475        /**
2476         * @event append
2477         * Fires when a new child node is appended
2478         * @param {Tree} tree The owner tree
2479         * @param {Node} this This node
2480         * @param {Node} node The newly appended node
2481         * @param {Number} index The index of the newly appended node
2482         */
2483        "append" : true,
2484        /**
2485         * @event remove
2486         * Fires when a child node is removed
2487         * @param {Tree} tree The owner tree
2488         * @param {Node} this This node
2489         * @param {Node} node The removed node
2490         */
2491        "remove" : true,
2492        /**
2493         * @event move
2494         * Fires when this node is moved to a new location in the tree
2495         * @param {Tree} tree The owner tree
2496         * @param {Node} this This node
2497         * @param {Node} oldParent The old parent of this node
2498         * @param {Node} newParent The new parent of this node
2499         * @param {Number} index The index it was moved to
2500         */
2501        "move" : true,
2502        /**
2503         * @event insert
2504         * Fires when a new child node is inserted.
2505         * @param {Tree} tree The owner tree
2506         * @param {Node} this This node
2507         * @param {Node} node The child node inserted
2508         * @param {Node} refNode The child node the node was inserted before
2509         */
2510        "insert" : true,
2511        /**
2512         * @event beforeappend
2513         * Fires before a new child is appended, return false to cancel the append.
2514         * @param {Tree} tree The owner tree
2515         * @param {Node} this This node
2516         * @param {Node} node The child node to be appended
2517         */
2518        "beforeappend" : true,
2519        /**
2520         * @event beforeremove
2521         * Fires before a child is removed, return false to cancel the remove.
2522         * @param {Tree} tree The owner tree
2523         * @param {Node} this This node
2524         * @param {Node} node The child node to be removed
2525         */
2526        "beforeremove" : true,
2527        /**
2528         * @event beforemove
2529         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2530         * @param {Tree} tree The owner tree
2531         * @param {Node} this This node
2532         * @param {Node} oldParent The parent of this node
2533         * @param {Node} newParent The new parent this node is moving to
2534         * @param {Number} index The index it is being moved to
2535         */
2536        "beforemove" : true,
2537        /**
2538         * @event beforeinsert
2539         * Fires before a new child is inserted, return false to cancel the insert.
2540         * @param {Tree} tree The owner tree
2541         * @param {Node} this This node
2542         * @param {Node} node The child node to be inserted
2543         * @param {Node} refNode The child node the node is being inserted before
2544         */
2545        "beforeinsert" : true
2546    });
2547     this.listeners = this.attributes.listeners;
2548     Roo.data.Node.superclass.constructor.call(this);
2549 };
2550
2551 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2552     fireEvent : function(evtName){
2553         // first do standard event for this node
2554         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2555             return false;
2556         }
2557         // then bubble it up to the tree if the event wasn't cancelled
2558         var ot = this.getOwnerTree();
2559         if(ot){
2560             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2561                 return false;
2562             }
2563         }
2564         return true;
2565     },
2566
2567     /**
2568      * Returns true if this node is a leaf
2569      * @return {Boolean}
2570      */
2571     isLeaf : function(){
2572         return this.leaf === true;
2573     },
2574
2575     // private
2576     setFirstChild : function(node){
2577         this.firstChild = node;
2578     },
2579
2580     //private
2581     setLastChild : function(node){
2582         this.lastChild = node;
2583     },
2584
2585
2586     /**
2587      * Returns true if this node is the last child of its parent
2588      * @return {Boolean}
2589      */
2590     isLast : function(){
2591        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2592     },
2593
2594     /**
2595      * Returns true if this node is the first child of its parent
2596      * @return {Boolean}
2597      */
2598     isFirst : function(){
2599        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2600     },
2601
2602     hasChildNodes : function(){
2603         return !this.isLeaf() && this.childNodes.length > 0;
2604     },
2605
2606     /**
2607      * Insert node(s) as the last child node of this node.
2608      * @param {Node/Array} node The node or Array of nodes to append
2609      * @return {Node} The appended node if single append, or null if an array was passed
2610      */
2611     appendChild : function(node){
2612         var multi = false;
2613         if(node instanceof Array){
2614             multi = node;
2615         }else if(arguments.length > 1){
2616             multi = arguments;
2617         }
2618         
2619         // if passed an array or multiple args do them one by one
2620         if(multi){
2621             for(var i = 0, len = multi.length; i < len; i++) {
2622                 this.appendChild(multi[i]);
2623             }
2624         }else{
2625             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2626                 return false;
2627             }
2628             var index = this.childNodes.length;
2629             var oldParent = node.parentNode;
2630             // it's a move, make sure we move it cleanly
2631             if(oldParent){
2632                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2633                     return false;
2634                 }
2635                 oldParent.removeChild(node);
2636             }
2637             
2638             index = this.childNodes.length;
2639             if(index == 0){
2640                 this.setFirstChild(node);
2641             }
2642             this.childNodes.push(node);
2643             node.parentNode = this;
2644             var ps = this.childNodes[index-1];
2645             if(ps){
2646                 node.previousSibling = ps;
2647                 ps.nextSibling = node;
2648             }else{
2649                 node.previousSibling = null;
2650             }
2651             node.nextSibling = null;
2652             this.setLastChild(node);
2653             node.setOwnerTree(this.getOwnerTree());
2654             this.fireEvent("append", this.ownerTree, this, node, index);
2655             if(this.ownerTree) {
2656                 this.ownerTree.fireEvent("appendnode", this, node, index);
2657             }
2658             if(oldParent){
2659                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2660             }
2661             return node;
2662         }
2663     },
2664
2665     /**
2666      * Removes a child node from this node.
2667      * @param {Node} node The node to remove
2668      * @return {Node} The removed node
2669      */
2670     removeChild : function(node){
2671         var index = this.childNodes.indexOf(node);
2672         if(index == -1){
2673             return false;
2674         }
2675         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2676             return false;
2677         }
2678
2679         // remove it from childNodes collection
2680         this.childNodes.splice(index, 1);
2681
2682         // update siblings
2683         if(node.previousSibling){
2684             node.previousSibling.nextSibling = node.nextSibling;
2685         }
2686         if(node.nextSibling){
2687             node.nextSibling.previousSibling = node.previousSibling;
2688         }
2689
2690         // update child refs
2691         if(this.firstChild == node){
2692             this.setFirstChild(node.nextSibling);
2693         }
2694         if(this.lastChild == node){
2695             this.setLastChild(node.previousSibling);
2696         }
2697
2698         node.setOwnerTree(null);
2699         // clear any references from the node
2700         node.parentNode = null;
2701         node.previousSibling = null;
2702         node.nextSibling = null;
2703         this.fireEvent("remove", this.ownerTree, this, node);
2704         return node;
2705     },
2706
2707     /**
2708      * Inserts the first node before the second node in this nodes childNodes collection.
2709      * @param {Node} node The node to insert
2710      * @param {Node} refNode The node to insert before (if null the node is appended)
2711      * @return {Node} The inserted node
2712      */
2713     insertBefore : function(node, refNode){
2714         if(!refNode){ // like standard Dom, refNode can be null for append
2715             return this.appendChild(node);
2716         }
2717         // nothing to do
2718         if(node == refNode){
2719             return false;
2720         }
2721
2722         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2723             return false;
2724         }
2725         var index = this.childNodes.indexOf(refNode);
2726         var oldParent = node.parentNode;
2727         var refIndex = index;
2728
2729         // when moving internally, indexes will change after remove
2730         if(oldParent == this && this.childNodes.indexOf(node) < index){
2731             refIndex--;
2732         }
2733
2734         // it's a move, make sure we move it cleanly
2735         if(oldParent){
2736             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2737                 return false;
2738             }
2739             oldParent.removeChild(node);
2740         }
2741         if(refIndex == 0){
2742             this.setFirstChild(node);
2743         }
2744         this.childNodes.splice(refIndex, 0, node);
2745         node.parentNode = this;
2746         var ps = this.childNodes[refIndex-1];
2747         if(ps){
2748             node.previousSibling = ps;
2749             ps.nextSibling = node;
2750         }else{
2751             node.previousSibling = null;
2752         }
2753         node.nextSibling = refNode;
2754         refNode.previousSibling = node;
2755         node.setOwnerTree(this.getOwnerTree());
2756         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2757         if(oldParent){
2758             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2759         }
2760         return node;
2761     },
2762
2763     /**
2764      * Returns the child node at the specified index.
2765      * @param {Number} index
2766      * @return {Node}
2767      */
2768     item : function(index){
2769         return this.childNodes[index];
2770     },
2771
2772     /**
2773      * Replaces one child node in this node with another.
2774      * @param {Node} newChild The replacement node
2775      * @param {Node} oldChild The node to replace
2776      * @return {Node} The replaced node
2777      */
2778     replaceChild : function(newChild, oldChild){
2779         this.insertBefore(newChild, oldChild);
2780         this.removeChild(oldChild);
2781         return oldChild;
2782     },
2783
2784     /**
2785      * Returns the index of a child node
2786      * @param {Node} node
2787      * @return {Number} The index of the node or -1 if it was not found
2788      */
2789     indexOf : function(child){
2790         return this.childNodes.indexOf(child);
2791     },
2792
2793     /**
2794      * Returns the tree this node is in.
2795      * @return {Tree}
2796      */
2797     getOwnerTree : function(){
2798         // if it doesn't have one, look for one
2799         if(!this.ownerTree){
2800             var p = this;
2801             while(p){
2802                 if(p.ownerTree){
2803                     this.ownerTree = p.ownerTree;
2804                     break;
2805                 }
2806                 p = p.parentNode;
2807             }
2808         }
2809         return this.ownerTree;
2810     },
2811
2812     /**
2813      * Returns depth of this node (the root node has a depth of 0)
2814      * @return {Number}
2815      */
2816     getDepth : function(){
2817         var depth = 0;
2818         var p = this;
2819         while(p.parentNode){
2820             ++depth;
2821             p = p.parentNode;
2822         }
2823         return depth;
2824     },
2825
2826     // private
2827     setOwnerTree : function(tree){
2828         // if it's move, we need to update everyone
2829         if(tree != this.ownerTree){
2830             if(this.ownerTree){
2831                 this.ownerTree.unregisterNode(this);
2832             }
2833             this.ownerTree = tree;
2834             var cs = this.childNodes;
2835             for(var i = 0, len = cs.length; i < len; i++) {
2836                 cs[i].setOwnerTree(tree);
2837             }
2838             if(tree){
2839                 tree.registerNode(this);
2840             }
2841         }
2842     },
2843
2844     /**
2845      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2846      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2847      * @return {String} The path
2848      */
2849     getPath : function(attr){
2850         attr = attr || "id";
2851         var p = this.parentNode;
2852         var b = [this.attributes[attr]];
2853         while(p){
2854             b.unshift(p.attributes[attr]);
2855             p = p.parentNode;
2856         }
2857         var sep = this.getOwnerTree().pathSeparator;
2858         return sep + b.join(sep);
2859     },
2860
2861     /**
2862      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2863      * function call will be the scope provided or the current node. The arguments to the function
2864      * will be the args provided or the current node. If the function returns false at any point,
2865      * the bubble is stopped.
2866      * @param {Function} fn The function to call
2867      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2868      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2869      */
2870     bubble : function(fn, scope, args){
2871         var p = this;
2872         while(p){
2873             if(fn.call(scope || p, args || p) === false){
2874                 break;
2875             }
2876             p = p.parentNode;
2877         }
2878     },
2879
2880     /**
2881      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2882      * function call will be the scope provided or the current node. The arguments to the function
2883      * will be the args provided or the current node. If the function returns false at any point,
2884      * the cascade is stopped on that branch.
2885      * @param {Function} fn The function to call
2886      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2887      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2888      */
2889     cascade : function(fn, scope, args){
2890         if(fn.call(scope || this, args || this) !== false){
2891             var cs = this.childNodes;
2892             for(var i = 0, len = cs.length; i < len; i++) {
2893                 cs[i].cascade(fn, scope, args);
2894             }
2895         }
2896     },
2897
2898     /**
2899      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2900      * function call will be the scope provided or the current node. The arguments to the function
2901      * will be the args provided or the current node. If the function returns false at any point,
2902      * the iteration stops.
2903      * @param {Function} fn The function to call
2904      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2905      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2906      */
2907     eachChild : function(fn, scope, args){
2908         var cs = this.childNodes;
2909         for(var i = 0, len = cs.length; i < len; i++) {
2910                 if(fn.call(scope || this, args || cs[i]) === false){
2911                     break;
2912                 }
2913         }
2914     },
2915
2916     /**
2917      * Finds the first child that has the attribute with the specified value.
2918      * @param {String} attribute The attribute name
2919      * @param {Mixed} value The value to search for
2920      * @return {Node} The found child or null if none was found
2921      */
2922     findChild : function(attribute, value){
2923         var cs = this.childNodes;
2924         for(var i = 0, len = cs.length; i < len; i++) {
2925                 if(cs[i].attributes[attribute] == value){
2926                     return cs[i];
2927                 }
2928         }
2929         return null;
2930     },
2931
2932     /**
2933      * Finds the first child by a custom function. The child matches if the function passed
2934      * returns true.
2935      * @param {Function} fn
2936      * @param {Object} scope (optional)
2937      * @return {Node} The found child or null if none was found
2938      */
2939     findChildBy : function(fn, scope){
2940         var cs = this.childNodes;
2941         for(var i = 0, len = cs.length; i < len; i++) {
2942                 if(fn.call(scope||cs[i], cs[i]) === true){
2943                     return cs[i];
2944                 }
2945         }
2946         return null;
2947     },
2948
2949     /**
2950      * Sorts this nodes children using the supplied sort function
2951      * @param {Function} fn
2952      * @param {Object} scope (optional)
2953      */
2954     sort : function(fn, scope){
2955         var cs = this.childNodes;
2956         var len = cs.length;
2957         if(len > 0){
2958             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2959             cs.sort(sortFn);
2960             for(var i = 0; i < len; i++){
2961                 var n = cs[i];
2962                 n.previousSibling = cs[i-1];
2963                 n.nextSibling = cs[i+1];
2964                 if(i == 0){
2965                     this.setFirstChild(n);
2966                 }
2967                 if(i == len-1){
2968                     this.setLastChild(n);
2969                 }
2970             }
2971         }
2972     },
2973
2974     /**
2975      * Returns true if this node is an ancestor (at any point) of the passed node.
2976      * @param {Node} node
2977      * @return {Boolean}
2978      */
2979     contains : function(node){
2980         return node.isAncestor(this);
2981     },
2982
2983     /**
2984      * Returns true if the passed node is an ancestor (at any point) of this node.
2985      * @param {Node} node
2986      * @return {Boolean}
2987      */
2988     isAncestor : function(node){
2989         var p = this.parentNode;
2990         while(p){
2991             if(p == node){
2992                 return true;
2993             }
2994             p = p.parentNode;
2995         }
2996         return false;
2997     },
2998
2999     toString : function(){
3000         return "[Node"+(this.id?" "+this.id:"")+"]";
3001     }
3002 });/*
3003  * Based on:
3004  * Ext JS Library 1.1.1
3005  * Copyright(c) 2006-2007, Ext JS, LLC.
3006  *
3007  * Originally Released Under LGPL - original licence link has changed is not relivant.
3008  *
3009  * Fork - LGPL
3010  * <script type="text/javascript">
3011  */
3012
3013
3014 /**
3015  * @class Roo.Shadow
3016  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3017  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3018  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3019  * @constructor
3020  * Create a new Shadow
3021  * @param {Object} config The config object
3022  */
3023 Roo.Shadow = function(config){
3024     Roo.apply(this, config);
3025     if(typeof this.mode != "string"){
3026         this.mode = this.defaultMode;
3027     }
3028     var o = this.offset, a = {h: 0};
3029     var rad = Math.floor(this.offset/2);
3030     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3031         case "drop":
3032             a.w = 0;
3033             a.l = a.t = o;
3034             a.t -= 1;
3035             if(Roo.isIE){
3036                 a.l -= this.offset + rad;
3037                 a.t -= this.offset + rad;
3038                 a.w -= rad;
3039                 a.h -= rad;
3040                 a.t += 1;
3041             }
3042         break;
3043         case "sides":
3044             a.w = (o*2);
3045             a.l = -o;
3046             a.t = o-1;
3047             if(Roo.isIE){
3048                 a.l -= (this.offset - rad);
3049                 a.t -= this.offset + rad;
3050                 a.l += 1;
3051                 a.w -= (this.offset - rad)*2;
3052                 a.w -= rad + 1;
3053                 a.h -= 1;
3054             }
3055         break;
3056         case "frame":
3057             a.w = a.h = (o*2);
3058             a.l = a.t = -o;
3059             a.t += 1;
3060             a.h -= 2;
3061             if(Roo.isIE){
3062                 a.l -= (this.offset - rad);
3063                 a.t -= (this.offset - rad);
3064                 a.l += 1;
3065                 a.w -= (this.offset + rad + 1);
3066                 a.h -= (this.offset + rad);
3067                 a.h += 1;
3068             }
3069         break;
3070     };
3071
3072     this.adjusts = a;
3073 };
3074
3075 Roo.Shadow.prototype = {
3076     /**
3077      * @cfg {String} mode
3078      * The shadow display mode.  Supports the following options:<br />
3079      * sides: Shadow displays on both sides and bottom only<br />
3080      * frame: Shadow displays equally on all four sides<br />
3081      * drop: Traditional bottom-right drop shadow (default)
3082      */
3083     mode: false,
3084     /**
3085      * @cfg {String} offset
3086      * The number of pixels to offset the shadow from the element (defaults to 4)
3087      */
3088     offset: 4,
3089
3090     // private
3091     defaultMode: "drop",
3092
3093     /**
3094      * Displays the shadow under the target element
3095      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3096      */
3097     show : function(target){
3098         target = Roo.get(target);
3099         if(!this.el){
3100             this.el = Roo.Shadow.Pool.pull();
3101             if(this.el.dom.nextSibling != target.dom){
3102                 this.el.insertBefore(target);
3103             }
3104         }
3105         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3106         if(Roo.isIE){
3107             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3108         }
3109         this.realign(
3110             target.getLeft(true),
3111             target.getTop(true),
3112             target.getWidth(),
3113             target.getHeight()
3114         );
3115         this.el.dom.style.display = "block";
3116     },
3117
3118     /**
3119      * Returns true if the shadow is visible, else false
3120      */
3121     isVisible : function(){
3122         return this.el ? true : false;  
3123     },
3124
3125     /**
3126      * Direct alignment when values are already available. Show must be called at least once before
3127      * calling this method to ensure it is initialized.
3128      * @param {Number} left The target element left position
3129      * @param {Number} top The target element top position
3130      * @param {Number} width The target element width
3131      * @param {Number} height The target element height
3132      */
3133     realign : function(l, t, w, h){
3134         if(!this.el){
3135             return;
3136         }
3137         var a = this.adjusts, d = this.el.dom, s = d.style;
3138         var iea = 0;
3139         s.left = (l+a.l)+"px";
3140         s.top = (t+a.t)+"px";
3141         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3142  
3143         if(s.width != sws || s.height != shs){
3144             s.width = sws;
3145             s.height = shs;
3146             if(!Roo.isIE){
3147                 var cn = d.childNodes;
3148                 var sww = Math.max(0, (sw-12))+"px";
3149                 cn[0].childNodes[1].style.width = sww;
3150                 cn[1].childNodes[1].style.width = sww;
3151                 cn[2].childNodes[1].style.width = sww;
3152                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3153             }
3154         }
3155     },
3156
3157     /**
3158      * Hides this shadow
3159      */
3160     hide : function(){
3161         if(this.el){
3162             this.el.dom.style.display = "none";
3163             Roo.Shadow.Pool.push(this.el);
3164             delete this.el;
3165         }
3166     },
3167
3168     /**
3169      * Adjust the z-index of this shadow
3170      * @param {Number} zindex The new z-index
3171      */
3172     setZIndex : function(z){
3173         this.zIndex = z;
3174         if(this.el){
3175             this.el.setStyle("z-index", z);
3176         }
3177     }
3178 };
3179
3180 // Private utility class that manages the internal Shadow cache
3181 Roo.Shadow.Pool = function(){
3182     var p = [];
3183     var markup = Roo.isIE ?
3184                  '<div class="x-ie-shadow"></div>' :
3185                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3186     return {
3187         pull : function(){
3188             var sh = p.shift();
3189             if(!sh){
3190                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3191                 sh.autoBoxAdjust = false;
3192             }
3193             return sh;
3194         },
3195
3196         push : function(sh){
3197             p.push(sh);
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210
3211
3212 /**
3213  * @class Roo.SplitBar
3214  * @extends Roo.util.Observable
3215  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3216  * <br><br>
3217  * Usage:
3218  * <pre><code>
3219 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3220                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3221 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3222 split.minSize = 100;
3223 split.maxSize = 600;
3224 split.animate = true;
3225 split.on('moved', splitterMoved);
3226 </code></pre>
3227  * @constructor
3228  * Create a new SplitBar
3229  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3230  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3231  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3232  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3233                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3234                         position of the SplitBar).
3235  */
3236 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3237     
3238     /** @private */
3239     this.el = Roo.get(dragElement, true);
3240     this.el.dom.unselectable = "on";
3241     /** @private */
3242     this.resizingEl = Roo.get(resizingElement, true);
3243
3244     /**
3245      * @private
3246      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3247      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3248      * @type Number
3249      */
3250     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3251     
3252     /**
3253      * The minimum size of the resizing element. (Defaults to 0)
3254      * @type Number
3255      */
3256     this.minSize = 0;
3257     
3258     /**
3259      * The maximum size of the resizing element. (Defaults to 2000)
3260      * @type Number
3261      */
3262     this.maxSize = 2000;
3263     
3264     /**
3265      * Whether to animate the transition to the new size
3266      * @type Boolean
3267      */
3268     this.animate = false;
3269     
3270     /**
3271      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3272      * @type Boolean
3273      */
3274     this.useShim = false;
3275     
3276     /** @private */
3277     this.shim = null;
3278     
3279     if(!existingProxy){
3280         /** @private */
3281         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3282     }else{
3283         this.proxy = Roo.get(existingProxy).dom;
3284     }
3285     /** @private */
3286     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3287     
3288     /** @private */
3289     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3290     
3291     /** @private */
3292     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3293     
3294     /** @private */
3295     this.dragSpecs = {};
3296     
3297     /**
3298      * @private The adapter to use to positon and resize elements
3299      */
3300     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3301     this.adapter.init(this);
3302     
3303     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3304         /** @private */
3305         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3306         this.el.addClass("x-splitbar-h");
3307     }else{
3308         /** @private */
3309         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3310         this.el.addClass("x-splitbar-v");
3311     }
3312     
3313     this.addEvents({
3314         /**
3315          * @event resize
3316          * Fires when the splitter is moved (alias for {@link #event-moved})
3317          * @param {Roo.SplitBar} this
3318          * @param {Number} newSize the new width or height
3319          */
3320         "resize" : true,
3321         /**
3322          * @event moved
3323          * Fires when the splitter is moved
3324          * @param {Roo.SplitBar} this
3325          * @param {Number} newSize the new width or height
3326          */
3327         "moved" : true,
3328         /**
3329          * @event beforeresize
3330          * Fires before the splitter is dragged
3331          * @param {Roo.SplitBar} this
3332          */
3333         "beforeresize" : true,
3334
3335         "beforeapply" : true
3336     });
3337
3338     Roo.util.Observable.call(this);
3339 };
3340
3341 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3342     onStartProxyDrag : function(x, y){
3343         this.fireEvent("beforeresize", this);
3344         if(!this.overlay){
3345             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3346             o.unselectable();
3347             o.enableDisplayMode("block");
3348             // all splitbars share the same overlay
3349             Roo.SplitBar.prototype.overlay = o;
3350         }
3351         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3352         this.overlay.show();
3353         Roo.get(this.proxy).setDisplayed("block");
3354         var size = this.adapter.getElementSize(this);
3355         this.activeMinSize = this.getMinimumSize();;
3356         this.activeMaxSize = this.getMaximumSize();;
3357         var c1 = size - this.activeMinSize;
3358         var c2 = Math.max(this.activeMaxSize - size, 0);
3359         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3360             this.dd.resetConstraints();
3361             this.dd.setXConstraint(
3362                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3363                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3364             );
3365             this.dd.setYConstraint(0, 0);
3366         }else{
3367             this.dd.resetConstraints();
3368             this.dd.setXConstraint(0, 0);
3369             this.dd.setYConstraint(
3370                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3371                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3372             );
3373          }
3374         this.dragSpecs.startSize = size;
3375         this.dragSpecs.startPoint = [x, y];
3376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3377     },
3378     
3379     /** 
3380      * @private Called after the drag operation by the DDProxy
3381      */
3382     onEndProxyDrag : function(e){
3383         Roo.get(this.proxy).setDisplayed(false);
3384         var endPoint = Roo.lib.Event.getXY(e);
3385         if(this.overlay){
3386             this.overlay.hide();
3387         }
3388         var newSize;
3389         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3390             newSize = this.dragSpecs.startSize + 
3391                 (this.placement == Roo.SplitBar.LEFT ?
3392                     endPoint[0] - this.dragSpecs.startPoint[0] :
3393                     this.dragSpecs.startPoint[0] - endPoint[0]
3394                 );
3395         }else{
3396             newSize = this.dragSpecs.startSize + 
3397                 (this.placement == Roo.SplitBar.TOP ?
3398                     endPoint[1] - this.dragSpecs.startPoint[1] :
3399                     this.dragSpecs.startPoint[1] - endPoint[1]
3400                 );
3401         }
3402         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3403         if(newSize != this.dragSpecs.startSize){
3404             if(this.fireEvent('beforeapply', this, newSize) !== false){
3405                 this.adapter.setElementSize(this, newSize);
3406                 this.fireEvent("moved", this, newSize);
3407                 this.fireEvent("resize", this, newSize);
3408             }
3409         }
3410     },
3411     
3412     /**
3413      * Get the adapter this SplitBar uses
3414      * @return The adapter object
3415      */
3416     getAdapter : function(){
3417         return this.adapter;
3418     },
3419     
3420     /**
3421      * Set the adapter this SplitBar uses
3422      * @param {Object} adapter A SplitBar adapter object
3423      */
3424     setAdapter : function(adapter){
3425         this.adapter = adapter;
3426         this.adapter.init(this);
3427     },
3428     
3429     /**
3430      * Gets the minimum size for the resizing element
3431      * @return {Number} The minimum size
3432      */
3433     getMinimumSize : function(){
3434         return this.minSize;
3435     },
3436     
3437     /**
3438      * Sets the minimum size for the resizing element
3439      * @param {Number} minSize The minimum size
3440      */
3441     setMinimumSize : function(minSize){
3442         this.minSize = minSize;
3443     },
3444     
3445     /**
3446      * Gets the maximum size for the resizing element
3447      * @return {Number} The maximum size
3448      */
3449     getMaximumSize : function(){
3450         return this.maxSize;
3451     },
3452     
3453     /**
3454      * Sets the maximum size for the resizing element
3455      * @param {Number} maxSize The maximum size
3456      */
3457     setMaximumSize : function(maxSize){
3458         this.maxSize = maxSize;
3459     },
3460     
3461     /**
3462      * Sets the initialize size for the resizing element
3463      * @param {Number} size The initial size
3464      */
3465     setCurrentSize : function(size){
3466         var oldAnimate = this.animate;
3467         this.animate = false;
3468         this.adapter.setElementSize(this, size);
3469         this.animate = oldAnimate;
3470     },
3471     
3472     /**
3473      * Destroy this splitbar. 
3474      * @param {Boolean} removeEl True to remove the element
3475      */
3476     destroy : function(removeEl){
3477         if(this.shim){
3478             this.shim.remove();
3479         }
3480         this.dd.unreg();
3481         this.proxy.parentNode.removeChild(this.proxy);
3482         if(removeEl){
3483             this.el.remove();
3484         }
3485     }
3486 });
3487
3488 /**
3489  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3490  */
3491 Roo.SplitBar.createProxy = function(dir){
3492     var proxy = new Roo.Element(document.createElement("div"));
3493     proxy.unselectable();
3494     var cls = 'x-splitbar-proxy';
3495     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3496     document.body.appendChild(proxy.dom);
3497     return proxy.dom;
3498 };
3499
3500 /** 
3501  * @class Roo.SplitBar.BasicLayoutAdapter
3502  * Default Adapter. It assumes the splitter and resizing element are not positioned
3503  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3504  */
3505 Roo.SplitBar.BasicLayoutAdapter = function(){
3506 };
3507
3508 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3509     // do nothing for now
3510     init : function(s){
3511     
3512     },
3513     /**
3514      * Called before drag operations to get the current size of the resizing element. 
3515      * @param {Roo.SplitBar} s The SplitBar using this adapter
3516      */
3517      getElementSize : function(s){
3518         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3519             return s.resizingEl.getWidth();
3520         }else{
3521             return s.resizingEl.getHeight();
3522         }
3523     },
3524     
3525     /**
3526      * Called after drag operations to set the size of the resizing element.
3527      * @param {Roo.SplitBar} s The SplitBar using this adapter
3528      * @param {Number} newSize The new size to set
3529      * @param {Function} onComplete A function to be invoked when resizing is complete
3530      */
3531     setElementSize : function(s, newSize, onComplete){
3532         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3533             if(!s.animate){
3534                 s.resizingEl.setWidth(newSize);
3535                 if(onComplete){
3536                     onComplete(s, newSize);
3537                 }
3538             }else{
3539                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3540             }
3541         }else{
3542             
3543             if(!s.animate){
3544                 s.resizingEl.setHeight(newSize);
3545                 if(onComplete){
3546                     onComplete(s, newSize);
3547                 }
3548             }else{
3549                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3550             }
3551         }
3552     }
3553 };
3554
3555 /** 
3556  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3557  * @extends Roo.SplitBar.BasicLayoutAdapter
3558  * Adapter that  moves the splitter element to align with the resized sizing element. 
3559  * Used with an absolute positioned SplitBar.
3560  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3561  * document.body, make sure you assign an id to the body element.
3562  */
3563 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3564     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3565     this.container = Roo.get(container);
3566 };
3567
3568 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3569     init : function(s){
3570         this.basic.init(s);
3571     },
3572     
3573     getElementSize : function(s){
3574         return this.basic.getElementSize(s);
3575     },
3576     
3577     setElementSize : function(s, newSize, onComplete){
3578         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3579     },
3580     
3581     moveSplitter : function(s){
3582         var yes = Roo.SplitBar;
3583         switch(s.placement){
3584             case yes.LEFT:
3585                 s.el.setX(s.resizingEl.getRight());
3586                 break;
3587             case yes.RIGHT:
3588                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3589                 break;
3590             case yes.TOP:
3591                 s.el.setY(s.resizingEl.getBottom());
3592                 break;
3593             case yes.BOTTOM:
3594                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3595                 break;
3596         }
3597     }
3598 };
3599
3600 /**
3601  * Orientation constant - Create a vertical SplitBar
3602  * @static
3603  * @type Number
3604  */
3605 Roo.SplitBar.VERTICAL = 1;
3606
3607 /**
3608  * Orientation constant - Create a horizontal SplitBar
3609  * @static
3610  * @type Number
3611  */
3612 Roo.SplitBar.HORIZONTAL = 2;
3613
3614 /**
3615  * Placement constant - The resizing element is to the left of the splitter element
3616  * @static
3617  * @type Number
3618  */
3619 Roo.SplitBar.LEFT = 1;
3620
3621 /**
3622  * Placement constant - The resizing element is to the right of the splitter element
3623  * @static
3624  * @type Number
3625  */
3626 Roo.SplitBar.RIGHT = 2;
3627
3628 /**
3629  * Placement constant - The resizing element is positioned above the splitter element
3630  * @static
3631  * @type Number
3632  */
3633 Roo.SplitBar.TOP = 3;
3634
3635 /**
3636  * Placement constant - The resizing element is positioned under splitter element
3637  * @static
3638  * @type Number
3639  */
3640 Roo.SplitBar.BOTTOM = 4;
3641 /*
3642  * Based on:
3643  * Ext JS Library 1.1.1
3644  * Copyright(c) 2006-2007, Ext JS, LLC.
3645  *
3646  * Originally Released Under LGPL - original licence link has changed is not relivant.
3647  *
3648  * Fork - LGPL
3649  * <script type="text/javascript">
3650  */
3651
3652 /**
3653  * @class Roo.View
3654  * @extends Roo.util.Observable
3655  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3656  * This class also supports single and multi selection modes. <br>
3657  * Create a data model bound view:
3658  <pre><code>
3659  var store = new Roo.data.Store(...);
3660
3661  var view = new Roo.View({
3662     el : "my-element",
3663     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3664  
3665     singleSelect: true,
3666     selectedClass: "ydataview-selected",
3667     store: store
3668  });
3669
3670  // listen for node click?
3671  view.on("click", function(vw, index, node, e){
3672  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3673  });
3674
3675  // load XML data
3676  dataModel.load("foobar.xml");
3677  </code></pre>
3678  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3679  * <br><br>
3680  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3681  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3682  * 
3683  * Note: old style constructor is still suported (container, template, config)
3684  * 
3685  * @constructor
3686  * Create a new View
3687  * @param {Object} config The config object
3688  * 
3689  */
3690 Roo.View = function(config, depreciated_tpl, depreciated_config){
3691     
3692     this.parent = false;
3693     
3694     if (typeof(depreciated_tpl) == 'undefined') {
3695         // new way.. - universal constructor.
3696         Roo.apply(this, config);
3697         this.el  = Roo.get(this.el);
3698     } else {
3699         // old format..
3700         this.el  = Roo.get(config);
3701         this.tpl = depreciated_tpl;
3702         Roo.apply(this, depreciated_config);
3703     }
3704     this.wrapEl  = this.el.wrap().wrap();
3705     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3706     
3707     
3708     if(typeof(this.tpl) == "string"){
3709         this.tpl = new Roo.Template(this.tpl);
3710     } else {
3711         // support xtype ctors..
3712         this.tpl = new Roo.factory(this.tpl, Roo);
3713     }
3714     
3715     
3716     this.tpl.compile();
3717     
3718     /** @private */
3719     this.addEvents({
3720         /**
3721          * @event beforeclick
3722          * Fires before a click is processed. Returns false to cancel the default action.
3723          * @param {Roo.View} this
3724          * @param {Number} index The index of the target node
3725          * @param {HTMLElement} node The target node
3726          * @param {Roo.EventObject} e The raw event object
3727          */
3728             "beforeclick" : true,
3729         /**
3730          * @event click
3731          * Fires when a template node is clicked.
3732          * @param {Roo.View} this
3733          * @param {Number} index The index of the target node
3734          * @param {HTMLElement} node The target node
3735          * @param {Roo.EventObject} e The raw event object
3736          */
3737             "click" : true,
3738         /**
3739          * @event dblclick
3740          * Fires when a template node is double clicked.
3741          * @param {Roo.View} this
3742          * @param {Number} index The index of the target node
3743          * @param {HTMLElement} node The target node
3744          * @param {Roo.EventObject} e The raw event object
3745          */
3746             "dblclick" : true,
3747         /**
3748          * @event contextmenu
3749          * Fires when a template node is right clicked.
3750          * @param {Roo.View} this
3751          * @param {Number} index The index of the target node
3752          * @param {HTMLElement} node The target node
3753          * @param {Roo.EventObject} e The raw event object
3754          */
3755             "contextmenu" : true,
3756         /**
3757          * @event selectionchange
3758          * Fires when the selected nodes change.
3759          * @param {Roo.View} this
3760          * @param {Array} selections Array of the selected nodes
3761          */
3762             "selectionchange" : true,
3763     
3764         /**
3765          * @event beforeselect
3766          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3767          * @param {Roo.View} this
3768          * @param {HTMLElement} node The node to be selected
3769          * @param {Array} selections Array of currently selected nodes
3770          */
3771             "beforeselect" : true,
3772         /**
3773          * @event preparedata
3774          * Fires on every row to render, to allow you to change the data.
3775          * @param {Roo.View} this
3776          * @param {Object} data to be rendered (change this)
3777          */
3778           "preparedata" : true
3779           
3780           
3781         });
3782
3783
3784
3785     this.el.on({
3786         "click": this.onClick,
3787         "dblclick": this.onDblClick,
3788         "contextmenu": this.onContextMenu,
3789         scope:this
3790     });
3791
3792     this.selections = [];
3793     this.nodes = [];
3794     this.cmp = new Roo.CompositeElementLite([]);
3795     if(this.store){
3796         this.store = Roo.factory(this.store, Roo.data);
3797         this.setStore(this.store, true);
3798     }
3799     
3800     if ( this.footer && this.footer.xtype) {
3801            
3802          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3803         
3804         this.footer.dataSource = this.store;
3805         this.footer.container = fctr;
3806         this.footer = Roo.factory(this.footer, Roo);
3807         fctr.insertFirst(this.el);
3808         
3809         // this is a bit insane - as the paging toolbar seems to detach the el..
3810 //        dom.parentNode.parentNode.parentNode
3811          // they get detached?
3812     }
3813     
3814     
3815     Roo.View.superclass.constructor.call(this);
3816     
3817     
3818 };
3819
3820 Roo.extend(Roo.View, Roo.util.Observable, {
3821     
3822      /**
3823      * @cfg {Roo.data.Store} store Data store to load data from.
3824      */
3825     store : false,
3826     
3827     /**
3828      * @cfg {String|Roo.Element} el The container element.
3829      */
3830     el : '',
3831     
3832     /**
3833      * @cfg {String|Roo.Template} tpl The template used by this View 
3834      */
3835     tpl : false,
3836     /**
3837      * @cfg {String} dataName the named area of the template to use as the data area
3838      *                          Works with domtemplates roo-name="name"
3839      */
3840     dataName: false,
3841     /**
3842      * @cfg {String} selectedClass The css class to add to selected nodes
3843      */
3844     selectedClass : "x-view-selected",
3845      /**
3846      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3847      */
3848     emptyText : "",
3849     
3850     /**
3851      * @cfg {String} text to display on mask (default Loading)
3852      */
3853     mask : false,
3854     /**
3855      * @cfg {Boolean} multiSelect Allow multiple selection
3856      */
3857     multiSelect : false,
3858     /**
3859      * @cfg {Boolean} singleSelect Allow single selection
3860      */
3861     singleSelect:  false,
3862     
3863     /**
3864      * @cfg {Boolean} toggleSelect - selecting 
3865      */
3866     toggleSelect : false,
3867     
3868     /**
3869      * @cfg {Boolean} tickable - selecting 
3870      */
3871     tickable : false,
3872     
3873     /**
3874      * Returns the element this view is bound to.
3875      * @return {Roo.Element}
3876      */
3877     getEl : function(){
3878         return this.wrapEl;
3879     },
3880     
3881     
3882
3883     /**
3884      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3885      */
3886     refresh : function(){
3887         //Roo.log('refresh');
3888         var t = this.tpl;
3889         
3890         // if we are using something like 'domtemplate', then
3891         // the what gets used is:
3892         // t.applySubtemplate(NAME, data, wrapping data..)
3893         // the outer template then get' applied with
3894         //     the store 'extra data'
3895         // and the body get's added to the
3896         //      roo-name="data" node?
3897         //      <span class='roo-tpl-{name}'></span> ?????
3898         
3899         
3900         
3901         this.clearSelections();
3902         this.el.update("");
3903         var html = [];
3904         var records = this.store.getRange();
3905         if(records.length < 1) {
3906             
3907             // is this valid??  = should it render a template??
3908             
3909             this.el.update(this.emptyText);
3910             return;
3911         }
3912         var el = this.el;
3913         if (this.dataName) {
3914             this.el.update(t.apply(this.store.meta)); //????
3915             el = this.el.child('.roo-tpl-' + this.dataName);
3916         }
3917         
3918         for(var i = 0, len = records.length; i < len; i++){
3919             var data = this.prepareData(records[i].data, i, records[i]);
3920             this.fireEvent("preparedata", this, data, i, records[i]);
3921             
3922             var d = Roo.apply({}, data);
3923             
3924             if(this.tickable){
3925                 Roo.apply(d, {'roo-id' : Roo.id()});
3926                 
3927                 var _this = this;
3928             
3929                 Roo.each(this.parent.item, function(item){
3930                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3931                         return;
3932                     }
3933                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3934                 });
3935             }
3936             
3937             html[html.length] = Roo.util.Format.trim(
3938                 this.dataName ?
3939                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3940                     t.apply(d)
3941             );
3942         }
3943         
3944         
3945         
3946         el.update(html.join(""));
3947         this.nodes = el.dom.childNodes;
3948         this.updateIndexes(0);
3949     },
3950     
3951
3952     /**
3953      * Function to override to reformat the data that is sent to
3954      * the template for each node.
3955      * DEPRICATED - use the preparedata event handler.
3956      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3957      * a JSON object for an UpdateManager bound view).
3958      */
3959     prepareData : function(data, index, record)
3960     {
3961         this.fireEvent("preparedata", this, data, index, record);
3962         return data;
3963     },
3964
3965     onUpdate : function(ds, record){
3966         // Roo.log('on update');   
3967         this.clearSelections();
3968         var index = this.store.indexOf(record);
3969         var n = this.nodes[index];
3970         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3971         n.parentNode.removeChild(n);
3972         this.updateIndexes(index, index);
3973     },
3974
3975     
3976     
3977 // --------- FIXME     
3978     onAdd : function(ds, records, index)
3979     {
3980         //Roo.log(['on Add', ds, records, index] );        
3981         this.clearSelections();
3982         if(this.nodes.length == 0){
3983             this.refresh();
3984             return;
3985         }
3986         var n = this.nodes[index];
3987         for(var i = 0, len = records.length; i < len; i++){
3988             var d = this.prepareData(records[i].data, i, records[i]);
3989             if(n){
3990                 this.tpl.insertBefore(n, d);
3991             }else{
3992                 
3993                 this.tpl.append(this.el, d);
3994             }
3995         }
3996         this.updateIndexes(index);
3997     },
3998
3999     onRemove : function(ds, record, index){
4000        // Roo.log('onRemove');
4001         this.clearSelections();
4002         var el = this.dataName  ?
4003             this.el.child('.roo-tpl-' + this.dataName) :
4004             this.el; 
4005         
4006         el.dom.removeChild(this.nodes[index]);
4007         this.updateIndexes(index);
4008     },
4009
4010     /**
4011      * Refresh an individual node.
4012      * @param {Number} index
4013      */
4014     refreshNode : function(index){
4015         this.onUpdate(this.store, this.store.getAt(index));
4016     },
4017
4018     updateIndexes : function(startIndex, endIndex){
4019         var ns = this.nodes;
4020         startIndex = startIndex || 0;
4021         endIndex = endIndex || ns.length - 1;
4022         for(var i = startIndex; i <= endIndex; i++){
4023             ns[i].nodeIndex = i;
4024         }
4025     },
4026
4027     /**
4028      * Changes the data store this view uses and refresh the view.
4029      * @param {Store} store
4030      */
4031     setStore : function(store, initial){
4032         if(!initial && this.store){
4033             this.store.un("datachanged", this.refresh);
4034             this.store.un("add", this.onAdd);
4035             this.store.un("remove", this.onRemove);
4036             this.store.un("update", this.onUpdate);
4037             this.store.un("clear", this.refresh);
4038             this.store.un("beforeload", this.onBeforeLoad);
4039             this.store.un("load", this.onLoad);
4040             this.store.un("loadexception", this.onLoad);
4041         }
4042         if(store){
4043           
4044             store.on("datachanged", this.refresh, this);
4045             store.on("add", this.onAdd, this);
4046             store.on("remove", this.onRemove, this);
4047             store.on("update", this.onUpdate, this);
4048             store.on("clear", this.refresh, this);
4049             store.on("beforeload", this.onBeforeLoad, this);
4050             store.on("load", this.onLoad, this);
4051             store.on("loadexception", this.onLoad, this);
4052         }
4053         
4054         if(store){
4055             this.refresh();
4056         }
4057     },
4058     /**
4059      * onbeforeLoad - masks the loading area.
4060      *
4061      */
4062     onBeforeLoad : function(store,opts)
4063     {
4064          //Roo.log('onBeforeLoad');   
4065         if (!opts.add) {
4066             this.el.update("");
4067         }
4068         this.el.mask(this.mask ? this.mask : "Loading" ); 
4069     },
4070     onLoad : function ()
4071     {
4072         this.el.unmask();
4073     },
4074     
4075
4076     /**
4077      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4078      * @param {HTMLElement} node
4079      * @return {HTMLElement} The template node
4080      */
4081     findItemFromChild : function(node){
4082         var el = this.dataName  ?
4083             this.el.child('.roo-tpl-' + this.dataName,true) :
4084             this.el.dom; 
4085         
4086         if(!node || node.parentNode == el){
4087                     return node;
4088             }
4089             var p = node.parentNode;
4090             while(p && p != el){
4091             if(p.parentNode == el){
4092                 return p;
4093             }
4094             p = p.parentNode;
4095         }
4096             return null;
4097     },
4098
4099     /** @ignore */
4100     onClick : function(e){
4101         var item = this.findItemFromChild(e.getTarget());
4102         if(item){
4103             var index = this.indexOf(item);
4104             if(this.onItemClick(item, index, e) !== false){
4105                 this.fireEvent("click", this, index, item, e);
4106             }
4107         }else{
4108             this.clearSelections();
4109         }
4110     },
4111
4112     /** @ignore */
4113     onContextMenu : function(e){
4114         var item = this.findItemFromChild(e.getTarget());
4115         if(item){
4116             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4117         }
4118     },
4119
4120     /** @ignore */
4121     onDblClick : function(e){
4122         var item = this.findItemFromChild(e.getTarget());
4123         if(item){
4124             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4125         }
4126     },
4127
4128     onItemClick : function(item, index, e)
4129     {
4130         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4131             return false;
4132         }
4133         if (this.toggleSelect) {
4134             var m = this.isSelected(item) ? 'unselect' : 'select';
4135             //Roo.log(m);
4136             var _t = this;
4137             _t[m](item, true, false);
4138             return true;
4139         }
4140         if(this.multiSelect || this.singleSelect){
4141             if(this.multiSelect && e.shiftKey && this.lastSelection){
4142                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4143             }else{
4144                 this.select(item, this.multiSelect && e.ctrlKey);
4145                 this.lastSelection = item;
4146             }
4147             
4148             if(!this.tickable){
4149                 e.preventDefault();
4150             }
4151             
4152         }
4153         return true;
4154     },
4155
4156     /**
4157      * Get the number of selected nodes.
4158      * @return {Number}
4159      */
4160     getSelectionCount : function(){
4161         return this.selections.length;
4162     },
4163
4164     /**
4165      * Get the currently selected nodes.
4166      * @return {Array} An array of HTMLElements
4167      */
4168     getSelectedNodes : function(){
4169         return this.selections;
4170     },
4171
4172     /**
4173      * Get the indexes of the selected nodes.
4174      * @return {Array}
4175      */
4176     getSelectedIndexes : function(){
4177         var indexes = [], s = this.selections;
4178         for(var i = 0, len = s.length; i < len; i++){
4179             indexes.push(s[i].nodeIndex);
4180         }
4181         return indexes;
4182     },
4183
4184     /**
4185      * Clear all selections
4186      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4187      */
4188     clearSelections : function(suppressEvent){
4189         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4190             this.cmp.elements = this.selections;
4191             this.cmp.removeClass(this.selectedClass);
4192             this.selections = [];
4193             if(!suppressEvent){
4194                 this.fireEvent("selectionchange", this, this.selections);
4195             }
4196         }
4197     },
4198
4199     /**
4200      * Returns true if the passed node is selected
4201      * @param {HTMLElement/Number} node The node or node index
4202      * @return {Boolean}
4203      */
4204     isSelected : function(node){
4205         var s = this.selections;
4206         if(s.length < 1){
4207             return false;
4208         }
4209         node = this.getNode(node);
4210         return s.indexOf(node) !== -1;
4211     },
4212
4213     /**
4214      * Selects nodes.
4215      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4216      * @param {Boolean} keepExisting (optional) true to keep existing selections
4217      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4218      */
4219     select : function(nodeInfo, keepExisting, suppressEvent){
4220         if(nodeInfo instanceof Array){
4221             if(!keepExisting){
4222                 this.clearSelections(true);
4223             }
4224             for(var i = 0, len = nodeInfo.length; i < len; i++){
4225                 this.select(nodeInfo[i], true, true);
4226             }
4227             return;
4228         } 
4229         var node = this.getNode(nodeInfo);
4230         if(!node || this.isSelected(node)){
4231             return; // already selected.
4232         }
4233         if(!keepExisting){
4234             this.clearSelections(true);
4235         }
4236         
4237         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4238             Roo.fly(node).addClass(this.selectedClass);
4239             this.selections.push(node);
4240             if(!suppressEvent){
4241                 this.fireEvent("selectionchange", this, this.selections);
4242             }
4243         }
4244         
4245         
4246     },
4247       /**
4248      * Unselects nodes.
4249      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4250      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4251      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4252      */
4253     unselect : function(nodeInfo, keepExisting, suppressEvent)
4254     {
4255         if(nodeInfo instanceof Array){
4256             Roo.each(this.selections, function(s) {
4257                 this.unselect(s, nodeInfo);
4258             }, this);
4259             return;
4260         }
4261         var node = this.getNode(nodeInfo);
4262         if(!node || !this.isSelected(node)){
4263             //Roo.log("not selected");
4264             return; // not selected.
4265         }
4266         // fireevent???
4267         var ns = [];
4268         Roo.each(this.selections, function(s) {
4269             if (s == node ) {
4270                 Roo.fly(node).removeClass(this.selectedClass);
4271
4272                 return;
4273             }
4274             ns.push(s);
4275         },this);
4276         
4277         this.selections= ns;
4278         this.fireEvent("selectionchange", this, this.selections);
4279     },
4280
4281     /**
4282      * Gets a template node.
4283      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4284      * @return {HTMLElement} The node or null if it wasn't found
4285      */
4286     getNode : function(nodeInfo){
4287         if(typeof nodeInfo == "string"){
4288             return document.getElementById(nodeInfo);
4289         }else if(typeof nodeInfo == "number"){
4290             return this.nodes[nodeInfo];
4291         }
4292         return nodeInfo;
4293     },
4294
4295     /**
4296      * Gets a range template nodes.
4297      * @param {Number} startIndex
4298      * @param {Number} endIndex
4299      * @return {Array} An array of nodes
4300      */
4301     getNodes : function(start, end){
4302         var ns = this.nodes;
4303         start = start || 0;
4304         end = typeof end == "undefined" ? ns.length - 1 : end;
4305         var nodes = [];
4306         if(start <= end){
4307             for(var i = start; i <= end; i++){
4308                 nodes.push(ns[i]);
4309             }
4310         } else{
4311             for(var i = start; i >= end; i--){
4312                 nodes.push(ns[i]);
4313             }
4314         }
4315         return nodes;
4316     },
4317
4318     /**
4319      * Finds the index of the passed node
4320      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4321      * @return {Number} The index of the node or -1
4322      */
4323     indexOf : function(node){
4324         node = this.getNode(node);
4325         if(typeof node.nodeIndex == "number"){
4326             return node.nodeIndex;
4327         }
4328         var ns = this.nodes;
4329         for(var i = 0, len = ns.length; i < len; i++){
4330             if(ns[i] == node){
4331                 return i;
4332             }
4333         }
4334         return -1;
4335     }
4336 });
4337 /*
4338  * Based on:
4339  * Ext JS Library 1.1.1
4340  * Copyright(c) 2006-2007, Ext JS, LLC.
4341  *
4342  * Originally Released Under LGPL - original licence link has changed is not relivant.
4343  *
4344  * Fork - LGPL
4345  * <script type="text/javascript">
4346  */
4347
4348 /**
4349  * @class Roo.JsonView
4350  * @extends Roo.View
4351  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4352 <pre><code>
4353 var view = new Roo.JsonView({
4354     container: "my-element",
4355     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4356     multiSelect: true, 
4357     jsonRoot: "data" 
4358 });
4359
4360 // listen for node click?
4361 view.on("click", function(vw, index, node, e){
4362     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4363 });
4364
4365 // direct load of JSON data
4366 view.load("foobar.php");
4367
4368 // Example from my blog list
4369 var tpl = new Roo.Template(
4370     '&lt;div class="entry"&gt;' +
4371     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4372     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4373     "&lt;/div&gt;&lt;hr /&gt;"
4374 );
4375
4376 var moreView = new Roo.JsonView({
4377     container :  "entry-list", 
4378     template : tpl,
4379     jsonRoot: "posts"
4380 });
4381 moreView.on("beforerender", this.sortEntries, this);
4382 moreView.load({
4383     url: "/blog/get-posts.php",
4384     params: "allposts=true",
4385     text: "Loading Blog Entries..."
4386 });
4387 </code></pre>
4388
4389 * Note: old code is supported with arguments : (container, template, config)
4390
4391
4392  * @constructor
4393  * Create a new JsonView
4394  * 
4395  * @param {Object} config The config object
4396  * 
4397  */
4398 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4399     
4400     
4401     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4402
4403     var um = this.el.getUpdateManager();
4404     um.setRenderer(this);
4405     um.on("update", this.onLoad, this);
4406     um.on("failure", this.onLoadException, this);
4407
4408     /**
4409      * @event beforerender
4410      * Fires before rendering of the downloaded JSON data.
4411      * @param {Roo.JsonView} this
4412      * @param {Object} data The JSON data loaded
4413      */
4414     /**
4415      * @event load
4416      * Fires when data is loaded.
4417      * @param {Roo.JsonView} this
4418      * @param {Object} data The JSON data loaded
4419      * @param {Object} response The raw Connect response object
4420      */
4421     /**
4422      * @event loadexception
4423      * Fires when loading fails.
4424      * @param {Roo.JsonView} this
4425      * @param {Object} response The raw Connect response object
4426      */
4427     this.addEvents({
4428         'beforerender' : true,
4429         'load' : true,
4430         'loadexception' : true
4431     });
4432 };
4433 Roo.extend(Roo.JsonView, Roo.View, {
4434     /**
4435      * @type {String} The root property in the loaded JSON object that contains the data
4436      */
4437     jsonRoot : "",
4438
4439     /**
4440      * Refreshes the view.
4441      */
4442     refresh : function(){
4443         this.clearSelections();
4444         this.el.update("");
4445         var html = [];
4446         var o = this.jsonData;
4447         if(o && o.length > 0){
4448             for(var i = 0, len = o.length; i < len; i++){
4449                 var data = this.prepareData(o[i], i, o);
4450                 html[html.length] = this.tpl.apply(data);
4451             }
4452         }else{
4453             html.push(this.emptyText);
4454         }
4455         this.el.update(html.join(""));
4456         this.nodes = this.el.dom.childNodes;
4457         this.updateIndexes(0);
4458     },
4459
4460     /**
4461      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4462      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4463      <pre><code>
4464      view.load({
4465          url: "your-url.php",
4466          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4467          callback: yourFunction,
4468          scope: yourObject, //(optional scope)
4469          discardUrl: false,
4470          nocache: false,
4471          text: "Loading...",
4472          timeout: 30,
4473          scripts: false
4474      });
4475      </code></pre>
4476      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4477      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4478      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4479      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4480      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4481      */
4482     load : function(){
4483         var um = this.el.getUpdateManager();
4484         um.update.apply(um, arguments);
4485     },
4486
4487     // note - render is a standard framework call...
4488     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4489     render : function(el, response){
4490         
4491         this.clearSelections();
4492         this.el.update("");
4493         var o;
4494         try{
4495             if (response != '') {
4496                 o = Roo.util.JSON.decode(response.responseText);
4497                 if(this.jsonRoot){
4498                     
4499                     o = o[this.jsonRoot];
4500                 }
4501             }
4502         } catch(e){
4503         }
4504         /**
4505          * The current JSON data or null
4506          */
4507         this.jsonData = o;
4508         this.beforeRender();
4509         this.refresh();
4510     },
4511
4512 /**
4513  * Get the number of records in the current JSON dataset
4514  * @return {Number}
4515  */
4516     getCount : function(){
4517         return this.jsonData ? this.jsonData.length : 0;
4518     },
4519
4520 /**
4521  * Returns the JSON object for the specified node(s)
4522  * @param {HTMLElement/Array} node The node or an array of nodes
4523  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4524  * you get the JSON object for the node
4525  */
4526     getNodeData : function(node){
4527         if(node instanceof Array){
4528             var data = [];
4529             for(var i = 0, len = node.length; i < len; i++){
4530                 data.push(this.getNodeData(node[i]));
4531             }
4532             return data;
4533         }
4534         return this.jsonData[this.indexOf(node)] || null;
4535     },
4536
4537     beforeRender : function(){
4538         this.snapshot = this.jsonData;
4539         if(this.sortInfo){
4540             this.sort.apply(this, this.sortInfo);
4541         }
4542         this.fireEvent("beforerender", this, this.jsonData);
4543     },
4544
4545     onLoad : function(el, o){
4546         this.fireEvent("load", this, this.jsonData, o);
4547     },
4548
4549     onLoadException : function(el, o){
4550         this.fireEvent("loadexception", this, o);
4551     },
4552
4553 /**
4554  * Filter the data by a specific property.
4555  * @param {String} property A property on your JSON objects
4556  * @param {String/RegExp} value Either string that the property values
4557  * should start with, or a RegExp to test against the property
4558  */
4559     filter : function(property, value){
4560         if(this.jsonData){
4561             var data = [];
4562             var ss = this.snapshot;
4563             if(typeof value == "string"){
4564                 var vlen = value.length;
4565                 if(vlen == 0){
4566                     this.clearFilter();
4567                     return;
4568                 }
4569                 value = value.toLowerCase();
4570                 for(var i = 0, len = ss.length; i < len; i++){
4571                     var o = ss[i];
4572                     if(o[property].substr(0, vlen).toLowerCase() == value){
4573                         data.push(o);
4574                     }
4575                 }
4576             } else if(value.exec){ // regex?
4577                 for(var i = 0, len = ss.length; i < len; i++){
4578                     var o = ss[i];
4579                     if(value.test(o[property])){
4580                         data.push(o);
4581                     }
4582                 }
4583             } else{
4584                 return;
4585             }
4586             this.jsonData = data;
4587             this.refresh();
4588         }
4589     },
4590
4591 /**
4592  * Filter by a function. The passed function will be called with each
4593  * object in the current dataset. If the function returns true the value is kept,
4594  * otherwise it is filtered.
4595  * @param {Function} fn
4596  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4597  */
4598     filterBy : function(fn, scope){
4599         if(this.jsonData){
4600             var data = [];
4601             var ss = this.snapshot;
4602             for(var i = 0, len = ss.length; i < len; i++){
4603                 var o = ss[i];
4604                 if(fn.call(scope || this, o)){
4605                     data.push(o);
4606                 }
4607             }
4608             this.jsonData = data;
4609             this.refresh();
4610         }
4611     },
4612
4613 /**
4614  * Clears the current filter.
4615  */
4616     clearFilter : function(){
4617         if(this.snapshot && this.jsonData != this.snapshot){
4618             this.jsonData = this.snapshot;
4619             this.refresh();
4620         }
4621     },
4622
4623
4624 /**
4625  * Sorts the data for this view and refreshes it.
4626  * @param {String} property A property on your JSON objects to sort on
4627  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4628  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4629  */
4630     sort : function(property, dir, sortType){
4631         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4632         if(this.jsonData){
4633             var p = property;
4634             var dsc = dir && dir.toLowerCase() == "desc";
4635             var f = function(o1, o2){
4636                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4637                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4638                 ;
4639                 if(v1 < v2){
4640                     return dsc ? +1 : -1;
4641                 } else if(v1 > v2){
4642                     return dsc ? -1 : +1;
4643                 } else{
4644                     return 0;
4645                 }
4646             };
4647             this.jsonData.sort(f);
4648             this.refresh();
4649             if(this.jsonData != this.snapshot){
4650                 this.snapshot.sort(f);
4651             }
4652         }
4653     }
4654 });/*
4655  * Based on:
4656  * Ext JS Library 1.1.1
4657  * Copyright(c) 2006-2007, Ext JS, LLC.
4658  *
4659  * Originally Released Under LGPL - original licence link has changed is not relivant.
4660  *
4661  * Fork - LGPL
4662  * <script type="text/javascript">
4663  */
4664  
4665
4666 /**
4667  * @class Roo.ColorPalette
4668  * @extends Roo.Component
4669  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4670  * Here's an example of typical usage:
4671  * <pre><code>
4672 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4673 cp.render('my-div');
4674
4675 cp.on('select', function(palette, selColor){
4676     // do something with selColor
4677 });
4678 </code></pre>
4679  * @constructor
4680  * Create a new ColorPalette
4681  * @param {Object} config The config object
4682  */
4683 Roo.ColorPalette = function(config){
4684     Roo.ColorPalette.superclass.constructor.call(this, config);
4685     this.addEvents({
4686         /**
4687              * @event select
4688              * Fires when a color is selected
4689              * @param {ColorPalette} this
4690              * @param {String} color The 6-digit color hex code (without the # symbol)
4691              */
4692         select: true
4693     });
4694
4695     if(this.handler){
4696         this.on("select", this.handler, this.scope, true);
4697     }
4698 };
4699 Roo.extend(Roo.ColorPalette, Roo.Component, {
4700     /**
4701      * @cfg {String} itemCls
4702      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4703      */
4704     itemCls : "x-color-palette",
4705     /**
4706      * @cfg {String} value
4707      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4708      * the hex codes are case-sensitive.
4709      */
4710     value : null,
4711     clickEvent:'click',
4712     // private
4713     ctype: "Roo.ColorPalette",
4714
4715     /**
4716      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4717      */
4718     allowReselect : false,
4719
4720     /**
4721      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4722      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4723      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4724      * of colors with the width setting until the box is symmetrical.</p>
4725      * <p>You can override individual colors if needed:</p>
4726      * <pre><code>
4727 var cp = new Roo.ColorPalette();
4728 cp.colors[0] = "FF0000";  // change the first box to red
4729 </code></pre>
4730
4731 Or you can provide a custom array of your own for complete control:
4732 <pre><code>
4733 var cp = new Roo.ColorPalette();
4734 cp.colors = ["000000", "993300", "333300"];
4735 </code></pre>
4736      * @type Array
4737      */
4738     colors : [
4739         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4740         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4741         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4742         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4743         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4744     ],
4745
4746     // private
4747     onRender : function(container, position){
4748         var t = new Roo.MasterTemplate(
4749             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4750         );
4751         var c = this.colors;
4752         for(var i = 0, len = c.length; i < len; i++){
4753             t.add([c[i]]);
4754         }
4755         var el = document.createElement("div");
4756         el.className = this.itemCls;
4757         t.overwrite(el);
4758         container.dom.insertBefore(el, position);
4759         this.el = Roo.get(el);
4760         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4761         if(this.clickEvent != 'click'){
4762             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4763         }
4764     },
4765
4766     // private
4767     afterRender : function(){
4768         Roo.ColorPalette.superclass.afterRender.call(this);
4769         if(this.value){
4770             var s = this.value;
4771             this.value = null;
4772             this.select(s);
4773         }
4774     },
4775
4776     // private
4777     handleClick : function(e, t){
4778         e.preventDefault();
4779         if(!this.disabled){
4780             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4781             this.select(c.toUpperCase());
4782         }
4783     },
4784
4785     /**
4786      * Selects the specified color in the palette (fires the select event)
4787      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4788      */
4789     select : function(color){
4790         color = color.replace("#", "");
4791         if(color != this.value || this.allowReselect){
4792             var el = this.el;
4793             if(this.value){
4794                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4795             }
4796             el.child("a.color-"+color).addClass("x-color-palette-sel");
4797             this.value = color;
4798             this.fireEvent("select", this, color);
4799         }
4800     }
4801 });/*
4802  * Based on:
4803  * Ext JS Library 1.1.1
4804  * Copyright(c) 2006-2007, Ext JS, LLC.
4805  *
4806  * Originally Released Under LGPL - original licence link has changed is not relivant.
4807  *
4808  * Fork - LGPL
4809  * <script type="text/javascript">
4810  */
4811  
4812 /**
4813  * @class Roo.DatePicker
4814  * @extends Roo.Component
4815  * Simple date picker class.
4816  * @constructor
4817  * Create a new DatePicker
4818  * @param {Object} config The config object
4819  */
4820 Roo.DatePicker = function(config){
4821     Roo.DatePicker.superclass.constructor.call(this, config);
4822
4823     this.value = config && config.value ?
4824                  config.value.clearTime() : new Date().clearTime();
4825
4826     this.addEvents({
4827         /**
4828              * @event select
4829              * Fires when a date is selected
4830              * @param {DatePicker} this
4831              * @param {Date} date The selected date
4832              */
4833         'select': true,
4834         /**
4835              * @event monthchange
4836              * Fires when the displayed month changes 
4837              * @param {DatePicker} this
4838              * @param {Date} date The selected month
4839              */
4840         'monthchange': true
4841     });
4842
4843     if(this.handler){
4844         this.on("select", this.handler,  this.scope || this);
4845     }
4846     // build the disabledDatesRE
4847     if(!this.disabledDatesRE && this.disabledDates){
4848         var dd = this.disabledDates;
4849         var re = "(?:";
4850         for(var i = 0; i < dd.length; i++){
4851             re += dd[i];
4852             if(i != dd.length-1) {
4853                 re += "|";
4854             }
4855         }
4856         this.disabledDatesRE = new RegExp(re + ")");
4857     }
4858 };
4859
4860 Roo.extend(Roo.DatePicker, Roo.Component, {
4861     /**
4862      * @cfg {String} todayText
4863      * The text to display on the button that selects the current date (defaults to "Today")
4864      */
4865     todayText : "Today",
4866     /**
4867      * @cfg {String} okText
4868      * The text to display on the ok button
4869      */
4870     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4871     /**
4872      * @cfg {String} cancelText
4873      * The text to display on the cancel button
4874      */
4875     cancelText : "Cancel",
4876     /**
4877      * @cfg {String} todayTip
4878      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4879      */
4880     todayTip : "{0} (Spacebar)",
4881     /**
4882      * @cfg {Date} minDate
4883      * Minimum allowable date (JavaScript date object, defaults to null)
4884      */
4885     minDate : null,
4886     /**
4887      * @cfg {Date} maxDate
4888      * Maximum allowable date (JavaScript date object, defaults to null)
4889      */
4890     maxDate : null,
4891     /**
4892      * @cfg {String} minText
4893      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4894      */
4895     minText : "This date is before the minimum date",
4896     /**
4897      * @cfg {String} maxText
4898      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4899      */
4900     maxText : "This date is after the maximum date",
4901     /**
4902      * @cfg {String} format
4903      * The default date format string which can be overriden for localization support.  The format must be
4904      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4905      */
4906     format : "m/d/y",
4907     /**
4908      * @cfg {Array} disabledDays
4909      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4910      */
4911     disabledDays : null,
4912     /**
4913      * @cfg {String} disabledDaysText
4914      * The tooltip to display when the date falls on a disabled day (defaults to "")
4915      */
4916     disabledDaysText : "",
4917     /**
4918      * @cfg {RegExp} disabledDatesRE
4919      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4920      */
4921     disabledDatesRE : null,
4922     /**
4923      * @cfg {String} disabledDatesText
4924      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4925      */
4926     disabledDatesText : "",
4927     /**
4928      * @cfg {Boolean} constrainToViewport
4929      * True to constrain the date picker to the viewport (defaults to true)
4930      */
4931     constrainToViewport : true,
4932     /**
4933      * @cfg {Array} monthNames
4934      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4935      */
4936     monthNames : Date.monthNames,
4937     /**
4938      * @cfg {Array} dayNames
4939      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4940      */
4941     dayNames : Date.dayNames,
4942     /**
4943      * @cfg {String} nextText
4944      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4945      */
4946     nextText: 'Next Month (Control+Right)',
4947     /**
4948      * @cfg {String} prevText
4949      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4950      */
4951     prevText: 'Previous Month (Control+Left)',
4952     /**
4953      * @cfg {String} monthYearText
4954      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4955      */
4956     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4957     /**
4958      * @cfg {Number} startDay
4959      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4960      */
4961     startDay : 0,
4962     /**
4963      * @cfg {Bool} showClear
4964      * Show a clear button (usefull for date form elements that can be blank.)
4965      */
4966     
4967     showClear: false,
4968     
4969     /**
4970      * Sets the value of the date field
4971      * @param {Date} value The date to set
4972      */
4973     setValue : function(value){
4974         var old = this.value;
4975         
4976         if (typeof(value) == 'string') {
4977          
4978             value = Date.parseDate(value, this.format);
4979         }
4980         if (!value) {
4981             value = new Date();
4982         }
4983         
4984         this.value = value.clearTime(true);
4985         if(this.el){
4986             this.update(this.value);
4987         }
4988     },
4989
4990     /**
4991      * Gets the current selected value of the date field
4992      * @return {Date} The selected date
4993      */
4994     getValue : function(){
4995         return this.value;
4996     },
4997
4998     // private
4999     focus : function(){
5000         if(this.el){
5001             this.update(this.activeDate);
5002         }
5003     },
5004
5005     // privateval
5006     onRender : function(container, position){
5007         
5008         var m = [
5009              '<table cellspacing="0">',
5010                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
5011                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5012         var dn = this.dayNames;
5013         for(var i = 0; i < 7; i++){
5014             var d = this.startDay+i;
5015             if(d > 6){
5016                 d = d-7;
5017             }
5018             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5019         }
5020         m[m.length] = "</tr></thead><tbody><tr>";
5021         for(var i = 0; i < 42; i++) {
5022             if(i % 7 == 0 && i != 0){
5023                 m[m.length] = "</tr><tr>";
5024             }
5025             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5026         }
5027         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5028             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5029
5030         var el = document.createElement("div");
5031         el.className = "x-date-picker";
5032         el.innerHTML = m.join("");
5033
5034         container.dom.insertBefore(el, position);
5035
5036         this.el = Roo.get(el);
5037         this.eventEl = Roo.get(el.firstChild);
5038
5039         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5040             handler: this.showPrevMonth,
5041             scope: this,
5042             preventDefault:true,
5043             stopDefault:true
5044         });
5045
5046         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5047             handler: this.showNextMonth,
5048             scope: this,
5049             preventDefault:true,
5050             stopDefault:true
5051         });
5052
5053         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5054
5055         this.monthPicker = this.el.down('div.x-date-mp');
5056         this.monthPicker.enableDisplayMode('block');
5057         
5058         var kn = new Roo.KeyNav(this.eventEl, {
5059             "left" : function(e){
5060                 e.ctrlKey ?
5061                     this.showPrevMonth() :
5062                     this.update(this.activeDate.add("d", -1));
5063             },
5064
5065             "right" : function(e){
5066                 e.ctrlKey ?
5067                     this.showNextMonth() :
5068                     this.update(this.activeDate.add("d", 1));
5069             },
5070
5071             "up" : function(e){
5072                 e.ctrlKey ?
5073                     this.showNextYear() :
5074                     this.update(this.activeDate.add("d", -7));
5075             },
5076
5077             "down" : function(e){
5078                 e.ctrlKey ?
5079                     this.showPrevYear() :
5080                     this.update(this.activeDate.add("d", 7));
5081             },
5082
5083             "pageUp" : function(e){
5084                 this.showNextMonth();
5085             },
5086
5087             "pageDown" : function(e){
5088                 this.showPrevMonth();
5089             },
5090
5091             "enter" : function(e){
5092                 e.stopPropagation();
5093                 return true;
5094             },
5095
5096             scope : this
5097         });
5098
5099         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5100
5101         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5102
5103         this.el.unselectable();
5104         
5105         this.cells = this.el.select("table.x-date-inner tbody td");
5106         this.textNodes = this.el.query("table.x-date-inner tbody span");
5107
5108         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5109             text: "&#160;",
5110             tooltip: this.monthYearText
5111         });
5112
5113         this.mbtn.on('click', this.showMonthPicker, this);
5114         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5115
5116
5117         var today = (new Date()).dateFormat(this.format);
5118         
5119         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5120         if (this.showClear) {
5121             baseTb.add( new Roo.Toolbar.Fill());
5122         }
5123         baseTb.add({
5124             text: String.format(this.todayText, today),
5125             tooltip: String.format(this.todayTip, today),
5126             handler: this.selectToday,
5127             scope: this
5128         });
5129         
5130         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5131             
5132         //});
5133         if (this.showClear) {
5134             
5135             baseTb.add( new Roo.Toolbar.Fill());
5136             baseTb.add({
5137                 text: '&#160;',
5138                 cls: 'x-btn-icon x-btn-clear',
5139                 handler: function() {
5140                     //this.value = '';
5141                     this.fireEvent("select", this, '');
5142                 },
5143                 scope: this
5144             });
5145         }
5146         
5147         
5148         if(Roo.isIE){
5149             this.el.repaint();
5150         }
5151         this.update(this.value);
5152     },
5153
5154     createMonthPicker : function(){
5155         if(!this.monthPicker.dom.firstChild){
5156             var buf = ['<table border="0" cellspacing="0">'];
5157             for(var i = 0; i < 6; i++){
5158                 buf.push(
5159                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5160                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5161                     i == 0 ?
5162                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5163                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5164                 );
5165             }
5166             buf.push(
5167                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5168                     this.okText,
5169                     '</button><button type="button" class="x-date-mp-cancel">',
5170                     this.cancelText,
5171                     '</button></td></tr>',
5172                 '</table>'
5173             );
5174             this.monthPicker.update(buf.join(''));
5175             this.monthPicker.on('click', this.onMonthClick, this);
5176             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5177
5178             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5179             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5180
5181             this.mpMonths.each(function(m, a, i){
5182                 i += 1;
5183                 if((i%2) == 0){
5184                     m.dom.xmonth = 5 + Math.round(i * .5);
5185                 }else{
5186                     m.dom.xmonth = Math.round((i-1) * .5);
5187                 }
5188             });
5189         }
5190     },
5191
5192     showMonthPicker : function(){
5193         this.createMonthPicker();
5194         var size = this.el.getSize();
5195         this.monthPicker.setSize(size);
5196         this.monthPicker.child('table').setSize(size);
5197
5198         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5199         this.updateMPMonth(this.mpSelMonth);
5200         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5201         this.updateMPYear(this.mpSelYear);
5202
5203         this.monthPicker.slideIn('t', {duration:.2});
5204     },
5205
5206     updateMPYear : function(y){
5207         this.mpyear = y;
5208         var ys = this.mpYears.elements;
5209         for(var i = 1; i <= 10; i++){
5210             var td = ys[i-1], y2;
5211             if((i%2) == 0){
5212                 y2 = y + Math.round(i * .5);
5213                 td.firstChild.innerHTML = y2;
5214                 td.xyear = y2;
5215             }else{
5216                 y2 = y - (5-Math.round(i * .5));
5217                 td.firstChild.innerHTML = y2;
5218                 td.xyear = y2;
5219             }
5220             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5221         }
5222     },
5223
5224     updateMPMonth : function(sm){
5225         this.mpMonths.each(function(m, a, i){
5226             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5227         });
5228     },
5229
5230     selectMPMonth: function(m){
5231         
5232     },
5233
5234     onMonthClick : function(e, t){
5235         e.stopEvent();
5236         var el = new Roo.Element(t), pn;
5237         if(el.is('button.x-date-mp-cancel')){
5238             this.hideMonthPicker();
5239         }
5240         else if(el.is('button.x-date-mp-ok')){
5241             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5242             this.hideMonthPicker();
5243         }
5244         else if(pn = el.up('td.x-date-mp-month', 2)){
5245             this.mpMonths.removeClass('x-date-mp-sel');
5246             pn.addClass('x-date-mp-sel');
5247             this.mpSelMonth = pn.dom.xmonth;
5248         }
5249         else if(pn = el.up('td.x-date-mp-year', 2)){
5250             this.mpYears.removeClass('x-date-mp-sel');
5251             pn.addClass('x-date-mp-sel');
5252             this.mpSelYear = pn.dom.xyear;
5253         }
5254         else if(el.is('a.x-date-mp-prev')){
5255             this.updateMPYear(this.mpyear-10);
5256         }
5257         else if(el.is('a.x-date-mp-next')){
5258             this.updateMPYear(this.mpyear+10);
5259         }
5260     },
5261
5262     onMonthDblClick : function(e, t){
5263         e.stopEvent();
5264         var el = new Roo.Element(t), pn;
5265         if(pn = el.up('td.x-date-mp-month', 2)){
5266             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5267             this.hideMonthPicker();
5268         }
5269         else if(pn = el.up('td.x-date-mp-year', 2)){
5270             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5271             this.hideMonthPicker();
5272         }
5273     },
5274
5275     hideMonthPicker : function(disableAnim){
5276         if(this.monthPicker){
5277             if(disableAnim === true){
5278                 this.monthPicker.hide();
5279             }else{
5280                 this.monthPicker.slideOut('t', {duration:.2});
5281             }
5282         }
5283     },
5284
5285     // private
5286     showPrevMonth : function(e){
5287         this.update(this.activeDate.add("mo", -1));
5288     },
5289
5290     // private
5291     showNextMonth : function(e){
5292         this.update(this.activeDate.add("mo", 1));
5293     },
5294
5295     // private
5296     showPrevYear : function(){
5297         this.update(this.activeDate.add("y", -1));
5298     },
5299
5300     // private
5301     showNextYear : function(){
5302         this.update(this.activeDate.add("y", 1));
5303     },
5304
5305     // private
5306     handleMouseWheel : function(e){
5307         var delta = e.getWheelDelta();
5308         if(delta > 0){
5309             this.showPrevMonth();
5310             e.stopEvent();
5311         } else if(delta < 0){
5312             this.showNextMonth();
5313             e.stopEvent();
5314         }
5315     },
5316
5317     // private
5318     handleDateClick : function(e, t){
5319         e.stopEvent();
5320         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5321             this.setValue(new Date(t.dateValue));
5322             this.fireEvent("select", this, this.value);
5323         }
5324     },
5325
5326     // private
5327     selectToday : function(){
5328         this.setValue(new Date().clearTime());
5329         this.fireEvent("select", this, this.value);
5330     },
5331
5332     // private
5333     update : function(date)
5334     {
5335         var vd = this.activeDate;
5336         this.activeDate = date;
5337         if(vd && this.el){
5338             var t = date.getTime();
5339             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5340                 this.cells.removeClass("x-date-selected");
5341                 this.cells.each(function(c){
5342                    if(c.dom.firstChild.dateValue == t){
5343                        c.addClass("x-date-selected");
5344                        setTimeout(function(){
5345                             try{c.dom.firstChild.focus();}catch(e){}
5346                        }, 50);
5347                        return false;
5348                    }
5349                 });
5350                 return;
5351             }
5352         }
5353         
5354         var days = date.getDaysInMonth();
5355         var firstOfMonth = date.getFirstDateOfMonth();
5356         var startingPos = firstOfMonth.getDay()-this.startDay;
5357
5358         if(startingPos <= this.startDay){
5359             startingPos += 7;
5360         }
5361
5362         var pm = date.add("mo", -1);
5363         var prevStart = pm.getDaysInMonth()-startingPos;
5364
5365         var cells = this.cells.elements;
5366         var textEls = this.textNodes;
5367         days += startingPos;
5368
5369         // convert everything to numbers so it's fast
5370         var day = 86400000;
5371         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5372         var today = new Date().clearTime().getTime();
5373         var sel = date.clearTime().getTime();
5374         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5375         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5376         var ddMatch = this.disabledDatesRE;
5377         var ddText = this.disabledDatesText;
5378         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5379         var ddaysText = this.disabledDaysText;
5380         var format = this.format;
5381
5382         var setCellClass = function(cal, cell){
5383             cell.title = "";
5384             var t = d.getTime();
5385             cell.firstChild.dateValue = t;
5386             if(t == today){
5387                 cell.className += " x-date-today";
5388                 cell.title = cal.todayText;
5389             }
5390             if(t == sel){
5391                 cell.className += " x-date-selected";
5392                 setTimeout(function(){
5393                     try{cell.firstChild.focus();}catch(e){}
5394                 }, 50);
5395             }
5396             // disabling
5397             if(t < min) {
5398                 cell.className = " x-date-disabled";
5399                 cell.title = cal.minText;
5400                 return;
5401             }
5402             if(t > max) {
5403                 cell.className = " x-date-disabled";
5404                 cell.title = cal.maxText;
5405                 return;
5406             }
5407             if(ddays){
5408                 if(ddays.indexOf(d.getDay()) != -1){
5409                     cell.title = ddaysText;
5410                     cell.className = " x-date-disabled";
5411                 }
5412             }
5413             if(ddMatch && format){
5414                 var fvalue = d.dateFormat(format);
5415                 if(ddMatch.test(fvalue)){
5416                     cell.title = ddText.replace("%0", fvalue);
5417                     cell.className = " x-date-disabled";
5418                 }
5419             }
5420         };
5421
5422         var i = 0;
5423         for(; i < startingPos; i++) {
5424             textEls[i].innerHTML = (++prevStart);
5425             d.setDate(d.getDate()+1);
5426             cells[i].className = "x-date-prevday";
5427             setCellClass(this, cells[i]);
5428         }
5429         for(; i < days; i++){
5430             intDay = i - startingPos + 1;
5431             textEls[i].innerHTML = (intDay);
5432             d.setDate(d.getDate()+1);
5433             cells[i].className = "x-date-active";
5434             setCellClass(this, cells[i]);
5435         }
5436         var extraDays = 0;
5437         for(; i < 42; i++) {
5438              textEls[i].innerHTML = (++extraDays);
5439              d.setDate(d.getDate()+1);
5440              cells[i].className = "x-date-nextday";
5441              setCellClass(this, cells[i]);
5442         }
5443
5444         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5445         this.fireEvent('monthchange', this, date);
5446         
5447         if(!this.internalRender){
5448             var main = this.el.dom.firstChild;
5449             var w = main.offsetWidth;
5450             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5451             Roo.fly(main).setWidth(w);
5452             this.internalRender = true;
5453             // opera does not respect the auto grow header center column
5454             // then, after it gets a width opera refuses to recalculate
5455             // without a second pass
5456             if(Roo.isOpera && !this.secondPass){
5457                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5458                 this.secondPass = true;
5459                 this.update.defer(10, this, [date]);
5460             }
5461         }
5462         
5463         
5464     }
5465 });        /*
5466  * Based on:
5467  * Ext JS Library 1.1.1
5468  * Copyright(c) 2006-2007, Ext JS, LLC.
5469  *
5470  * Originally Released Under LGPL - original licence link has changed is not relivant.
5471  *
5472  * Fork - LGPL
5473  * <script type="text/javascript">
5474  */
5475 /**
5476  * @class Roo.TabPanel
5477  * @extends Roo.util.Observable
5478  * A lightweight tab container.
5479  * <br><br>
5480  * Usage:
5481  * <pre><code>
5482 // basic tabs 1, built from existing content
5483 var tabs = new Roo.TabPanel("tabs1");
5484 tabs.addTab("script", "View Script");
5485 tabs.addTab("markup", "View Markup");
5486 tabs.activate("script");
5487
5488 // more advanced tabs, built from javascript
5489 var jtabs = new Roo.TabPanel("jtabs");
5490 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5491
5492 // set up the UpdateManager
5493 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5494 var updater = tab2.getUpdateManager();
5495 updater.setDefaultUrl("ajax1.htm");
5496 tab2.on('activate', updater.refresh, updater, true);
5497
5498 // Use setUrl for Ajax loading
5499 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5500 tab3.setUrl("ajax2.htm", null, true);
5501
5502 // Disabled tab
5503 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5504 tab4.disable();
5505
5506 jtabs.activate("jtabs-1");
5507  * </code></pre>
5508  * @constructor
5509  * Create a new TabPanel.
5510  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5511  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5512  */
5513 Roo.TabPanel = function(container, config){
5514     /**
5515     * The container element for this TabPanel.
5516     * @type Roo.Element
5517     */
5518     this.el = Roo.get(container, true);
5519     if(config){
5520         if(typeof config == "boolean"){
5521             this.tabPosition = config ? "bottom" : "top";
5522         }else{
5523             Roo.apply(this, config);
5524         }
5525     }
5526     if(this.tabPosition == "bottom"){
5527         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5528         this.el.addClass("x-tabs-bottom");
5529     }
5530     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5531     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5532     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5533     if(Roo.isIE){
5534         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5535     }
5536     if(this.tabPosition != "bottom"){
5537         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5538          * @type Roo.Element
5539          */
5540         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5541         this.el.addClass("x-tabs-top");
5542     }
5543     this.items = [];
5544
5545     this.bodyEl.setStyle("position", "relative");
5546
5547     this.active = null;
5548     this.activateDelegate = this.activate.createDelegate(this);
5549
5550     this.addEvents({
5551         /**
5552          * @event tabchange
5553          * Fires when the active tab changes
5554          * @param {Roo.TabPanel} this
5555          * @param {Roo.TabPanelItem} activePanel The new active tab
5556          */
5557         "tabchange": true,
5558         /**
5559          * @event beforetabchange
5560          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5561          * @param {Roo.TabPanel} this
5562          * @param {Object} e Set cancel to true on this object to cancel the tab change
5563          * @param {Roo.TabPanelItem} tab The tab being changed to
5564          */
5565         "beforetabchange" : true
5566     });
5567
5568     Roo.EventManager.onWindowResize(this.onResize, this);
5569     this.cpad = this.el.getPadding("lr");
5570     this.hiddenCount = 0;
5571
5572
5573     // toolbar on the tabbar support...
5574     if (this.toolbar) {
5575         var tcfg = this.toolbar;
5576         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5577         this.toolbar = new Roo.Toolbar(tcfg);
5578         if (Roo.isSafari) {
5579             var tbl = tcfg.container.child('table', true);
5580             tbl.setAttribute('width', '100%');
5581         }
5582         
5583     }
5584    
5585
5586
5587     Roo.TabPanel.superclass.constructor.call(this);
5588 };
5589
5590 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5591     /*
5592      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5593      */
5594     tabPosition : "top",
5595     /*
5596      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5597      */
5598     currentTabWidth : 0,
5599     /*
5600      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5601      */
5602     minTabWidth : 40,
5603     /*
5604      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5605      */
5606     maxTabWidth : 250,
5607     /*
5608      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5609      */
5610     preferredTabWidth : 175,
5611     /*
5612      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5613      */
5614     resizeTabs : false,
5615     /*
5616      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5617      */
5618     monitorResize : true,
5619     /*
5620      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5621      */
5622     toolbar : false,
5623
5624     /**
5625      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5626      * @param {String} id The id of the div to use <b>or create</b>
5627      * @param {String} text The text for the tab
5628      * @param {String} content (optional) Content to put in the TabPanelItem body
5629      * @param {Boolean} closable (optional) True to create a close icon on the tab
5630      * @return {Roo.TabPanelItem} The created TabPanelItem
5631      */
5632     addTab : function(id, text, content, closable){
5633         var item = new Roo.TabPanelItem(this, id, text, closable);
5634         this.addTabItem(item);
5635         if(content){
5636             item.setContent(content);
5637         }
5638         return item;
5639     },
5640
5641     /**
5642      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5643      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5644      * @return {Roo.TabPanelItem}
5645      */
5646     getTab : function(id){
5647         return this.items[id];
5648     },
5649
5650     /**
5651      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5652      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5653      */
5654     hideTab : function(id){
5655         var t = this.items[id];
5656         if(!t.isHidden()){
5657            t.setHidden(true);
5658            this.hiddenCount++;
5659            this.autoSizeTabs();
5660         }
5661     },
5662
5663     /**
5664      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5665      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5666      */
5667     unhideTab : function(id){
5668         var t = this.items[id];
5669         if(t.isHidden()){
5670            t.setHidden(false);
5671            this.hiddenCount--;
5672            this.autoSizeTabs();
5673         }
5674     },
5675
5676     /**
5677      * Adds an existing {@link Roo.TabPanelItem}.
5678      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5679      */
5680     addTabItem : function(item){
5681         this.items[item.id] = item;
5682         this.items.push(item);
5683         if(this.resizeTabs){
5684            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5685            this.autoSizeTabs();
5686         }else{
5687             item.autoSize();
5688         }
5689     },
5690
5691     /**
5692      * Removes a {@link Roo.TabPanelItem}.
5693      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5694      */
5695     removeTab : function(id){
5696         var items = this.items;
5697         var tab = items[id];
5698         if(!tab) { return; }
5699         var index = items.indexOf(tab);
5700         if(this.active == tab && items.length > 1){
5701             var newTab = this.getNextAvailable(index);
5702             if(newTab) {
5703                 newTab.activate();
5704             }
5705         }
5706         this.stripEl.dom.removeChild(tab.pnode.dom);
5707         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5708             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5709         }
5710         items.splice(index, 1);
5711         delete this.items[tab.id];
5712         tab.fireEvent("close", tab);
5713         tab.purgeListeners();
5714         this.autoSizeTabs();
5715     },
5716
5717     getNextAvailable : function(start){
5718         var items = this.items;
5719         var index = start;
5720         // look for a next tab that will slide over to
5721         // replace the one being removed
5722         while(index < items.length){
5723             var item = items[++index];
5724             if(item && !item.isHidden()){
5725                 return item;
5726             }
5727         }
5728         // if one isn't found select the previous tab (on the left)
5729         index = start;
5730         while(index >= 0){
5731             var item = items[--index];
5732             if(item && !item.isHidden()){
5733                 return item;
5734             }
5735         }
5736         return null;
5737     },
5738
5739     /**
5740      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5741      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5742      */
5743     disableTab : function(id){
5744         var tab = this.items[id];
5745         if(tab && this.active != tab){
5746             tab.disable();
5747         }
5748     },
5749
5750     /**
5751      * Enables a {@link Roo.TabPanelItem} that is disabled.
5752      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5753      */
5754     enableTab : function(id){
5755         var tab = this.items[id];
5756         tab.enable();
5757     },
5758
5759     /**
5760      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5761      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5762      * @return {Roo.TabPanelItem} The TabPanelItem.
5763      */
5764     activate : function(id){
5765         var tab = this.items[id];
5766         if(!tab){
5767             return null;
5768         }
5769         if(tab == this.active || tab.disabled){
5770             return tab;
5771         }
5772         var e = {};
5773         this.fireEvent("beforetabchange", this, e, tab);
5774         if(e.cancel !== true && !tab.disabled){
5775             if(this.active){
5776                 this.active.hide();
5777             }
5778             this.active = this.items[id];
5779             this.active.show();
5780             this.fireEvent("tabchange", this, this.active);
5781         }
5782         return tab;
5783     },
5784
5785     /**
5786      * Gets the active {@link Roo.TabPanelItem}.
5787      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5788      */
5789     getActiveTab : function(){
5790         return this.active;
5791     },
5792
5793     /**
5794      * Updates the tab body element to fit the height of the container element
5795      * for overflow scrolling
5796      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5797      */
5798     syncHeight : function(targetHeight){
5799         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5800         var bm = this.bodyEl.getMargins();
5801         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5802         this.bodyEl.setHeight(newHeight);
5803         return newHeight;
5804     },
5805
5806     onResize : function(){
5807         if(this.monitorResize){
5808             this.autoSizeTabs();
5809         }
5810     },
5811
5812     /**
5813      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5814      */
5815     beginUpdate : function(){
5816         this.updating = true;
5817     },
5818
5819     /**
5820      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5821      */
5822     endUpdate : function(){
5823         this.updating = false;
5824         this.autoSizeTabs();
5825     },
5826
5827     /**
5828      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5829      */
5830     autoSizeTabs : function(){
5831         var count = this.items.length;
5832         var vcount = count - this.hiddenCount;
5833         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5834             return;
5835         }
5836         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5837         var availWidth = Math.floor(w / vcount);
5838         var b = this.stripBody;
5839         if(b.getWidth() > w){
5840             var tabs = this.items;
5841             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5842             if(availWidth < this.minTabWidth){
5843                 /*if(!this.sleft){    // incomplete scrolling code
5844                     this.createScrollButtons();
5845                 }
5846                 this.showScroll();
5847                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5848             }
5849         }else{
5850             if(this.currentTabWidth < this.preferredTabWidth){
5851                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5852             }
5853         }
5854     },
5855
5856     /**
5857      * Returns the number of tabs in this TabPanel.
5858      * @return {Number}
5859      */
5860      getCount : function(){
5861          return this.items.length;
5862      },
5863
5864     /**
5865      * Resizes all the tabs to the passed width
5866      * @param {Number} The new width
5867      */
5868     setTabWidth : function(width){
5869         this.currentTabWidth = width;
5870         for(var i = 0, len = this.items.length; i < len; i++) {
5871                 if(!this.items[i].isHidden()) {
5872                 this.items[i].setWidth(width);
5873             }
5874         }
5875     },
5876
5877     /**
5878      * Destroys this TabPanel
5879      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5880      */
5881     destroy : function(removeEl){
5882         Roo.EventManager.removeResizeListener(this.onResize, this);
5883         for(var i = 0, len = this.items.length; i < len; i++){
5884             this.items[i].purgeListeners();
5885         }
5886         if(removeEl === true){
5887             this.el.update("");
5888             this.el.remove();
5889         }
5890     }
5891 });
5892
5893 /**
5894  * @class Roo.TabPanelItem
5895  * @extends Roo.util.Observable
5896  * Represents an individual item (tab plus body) in a TabPanel.
5897  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5898  * @param {String} id The id of this TabPanelItem
5899  * @param {String} text The text for the tab of this TabPanelItem
5900  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5901  */
5902 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5903     /**
5904      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5905      * @type Roo.TabPanel
5906      */
5907     this.tabPanel = tabPanel;
5908     /**
5909      * The id for this TabPanelItem
5910      * @type String
5911      */
5912     this.id = id;
5913     /** @private */
5914     this.disabled = false;
5915     /** @private */
5916     this.text = text;
5917     /** @private */
5918     this.loaded = false;
5919     this.closable = closable;
5920
5921     /**
5922      * The body element for this TabPanelItem.
5923      * @type Roo.Element
5924      */
5925     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5926     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5927     this.bodyEl.setStyle("display", "block");
5928     this.bodyEl.setStyle("zoom", "1");
5929     this.hideAction();
5930
5931     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5932     /** @private */
5933     this.el = Roo.get(els.el, true);
5934     this.inner = Roo.get(els.inner, true);
5935     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5936     this.pnode = Roo.get(els.el.parentNode, true);
5937     this.el.on("mousedown", this.onTabMouseDown, this);
5938     this.el.on("click", this.onTabClick, this);
5939     /** @private */
5940     if(closable){
5941         var c = Roo.get(els.close, true);
5942         c.dom.title = this.closeText;
5943         c.addClassOnOver("close-over");
5944         c.on("click", this.closeClick, this);
5945      }
5946
5947     this.addEvents({
5948          /**
5949          * @event activate
5950          * Fires when this tab becomes the active tab.
5951          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5952          * @param {Roo.TabPanelItem} this
5953          */
5954         "activate": true,
5955         /**
5956          * @event beforeclose
5957          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5958          * @param {Roo.TabPanelItem} this
5959          * @param {Object} e Set cancel to true on this object to cancel the close.
5960          */
5961         "beforeclose": true,
5962         /**
5963          * @event close
5964          * Fires when this tab is closed.
5965          * @param {Roo.TabPanelItem} this
5966          */
5967          "close": true,
5968         /**
5969          * @event deactivate
5970          * Fires when this tab is no longer the active tab.
5971          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5972          * @param {Roo.TabPanelItem} this
5973          */
5974          "deactivate" : true
5975     });
5976     this.hidden = false;
5977
5978     Roo.TabPanelItem.superclass.constructor.call(this);
5979 };
5980
5981 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5982     purgeListeners : function(){
5983        Roo.util.Observable.prototype.purgeListeners.call(this);
5984        this.el.removeAllListeners();
5985     },
5986     /**
5987      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5988      */
5989     show : function(){
5990         this.pnode.addClass("on");
5991         this.showAction();
5992         if(Roo.isOpera){
5993             this.tabPanel.stripWrap.repaint();
5994         }
5995         this.fireEvent("activate", this.tabPanel, this);
5996     },
5997
5998     /**
5999      * Returns true if this tab is the active tab.
6000      * @return {Boolean}
6001      */
6002     isActive : function(){
6003         return this.tabPanel.getActiveTab() == this;
6004     },
6005
6006     /**
6007      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6008      */
6009     hide : function(){
6010         this.pnode.removeClass("on");
6011         this.hideAction();
6012         this.fireEvent("deactivate", this.tabPanel, this);
6013     },
6014
6015     hideAction : function(){
6016         this.bodyEl.hide();
6017         this.bodyEl.setStyle("position", "absolute");
6018         this.bodyEl.setLeft("-20000px");
6019         this.bodyEl.setTop("-20000px");
6020     },
6021
6022     showAction : function(){
6023         this.bodyEl.setStyle("position", "relative");
6024         this.bodyEl.setTop("");
6025         this.bodyEl.setLeft("");
6026         this.bodyEl.show();
6027     },
6028
6029     /**
6030      * Set the tooltip for the tab.
6031      * @param {String} tooltip The tab's tooltip
6032      */
6033     setTooltip : function(text){
6034         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6035             this.textEl.dom.qtip = text;
6036             this.textEl.dom.removeAttribute('title');
6037         }else{
6038             this.textEl.dom.title = text;
6039         }
6040     },
6041
6042     onTabClick : function(e){
6043         e.preventDefault();
6044         this.tabPanel.activate(this.id);
6045     },
6046
6047     onTabMouseDown : function(e){
6048         e.preventDefault();
6049         this.tabPanel.activate(this.id);
6050     },
6051
6052     getWidth : function(){
6053         return this.inner.getWidth();
6054     },
6055
6056     setWidth : function(width){
6057         var iwidth = width - this.pnode.getPadding("lr");
6058         this.inner.setWidth(iwidth);
6059         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6060         this.pnode.setWidth(width);
6061     },
6062
6063     /**
6064      * Show or hide the tab
6065      * @param {Boolean} hidden True to hide or false to show.
6066      */
6067     setHidden : function(hidden){
6068         this.hidden = hidden;
6069         this.pnode.setStyle("display", hidden ? "none" : "");
6070     },
6071
6072     /**
6073      * Returns true if this tab is "hidden"
6074      * @return {Boolean}
6075      */
6076     isHidden : function(){
6077         return this.hidden;
6078     },
6079
6080     /**
6081      * Returns the text for this tab
6082      * @return {String}
6083      */
6084     getText : function(){
6085         return this.text;
6086     },
6087
6088     autoSize : function(){
6089         //this.el.beginMeasure();
6090         this.textEl.setWidth(1);
6091         /*
6092          *  #2804 [new] Tabs in Roojs
6093          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6094          */
6095         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6096         //this.el.endMeasure();
6097     },
6098
6099     /**
6100      * Sets the text for the tab (Note: this also sets the tooltip text)
6101      * @param {String} text The tab's text and tooltip
6102      */
6103     setText : function(text){
6104         this.text = text;
6105         this.textEl.update(text);
6106         this.setTooltip(text);
6107         if(!this.tabPanel.resizeTabs){
6108             this.autoSize();
6109         }
6110     },
6111     /**
6112      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6113      */
6114     activate : function(){
6115         this.tabPanel.activate(this.id);
6116     },
6117
6118     /**
6119      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6120      */
6121     disable : function(){
6122         if(this.tabPanel.active != this){
6123             this.disabled = true;
6124             this.pnode.addClass("disabled");
6125         }
6126     },
6127
6128     /**
6129      * Enables this TabPanelItem if it was previously disabled.
6130      */
6131     enable : function(){
6132         this.disabled = false;
6133         this.pnode.removeClass("disabled");
6134     },
6135
6136     /**
6137      * Sets the content for this TabPanelItem.
6138      * @param {String} content The content
6139      * @param {Boolean} loadScripts true to look for and load scripts
6140      */
6141     setContent : function(content, loadScripts){
6142         this.bodyEl.update(content, loadScripts);
6143     },
6144
6145     /**
6146      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6147      * @return {Roo.UpdateManager} The UpdateManager
6148      */
6149     getUpdateManager : function(){
6150         return this.bodyEl.getUpdateManager();
6151     },
6152
6153     /**
6154      * Set a URL to be used to load the content for this TabPanelItem.
6155      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6156      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6157      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6158      * @return {Roo.UpdateManager} The UpdateManager
6159      */
6160     setUrl : function(url, params, loadOnce){
6161         if(this.refreshDelegate){
6162             this.un('activate', this.refreshDelegate);
6163         }
6164         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6165         this.on("activate", this.refreshDelegate);
6166         return this.bodyEl.getUpdateManager();
6167     },
6168
6169     /** @private */
6170     _handleRefresh : function(url, params, loadOnce){
6171         if(!loadOnce || !this.loaded){
6172             var updater = this.bodyEl.getUpdateManager();
6173             updater.update(url, params, this._setLoaded.createDelegate(this));
6174         }
6175     },
6176
6177     /**
6178      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6179      *   Will fail silently if the setUrl method has not been called.
6180      *   This does not activate the panel, just updates its content.
6181      */
6182     refresh : function(){
6183         if(this.refreshDelegate){
6184            this.loaded = false;
6185            this.refreshDelegate();
6186         }
6187     },
6188
6189     /** @private */
6190     _setLoaded : function(){
6191         this.loaded = true;
6192     },
6193
6194     /** @private */
6195     closeClick : function(e){
6196         var o = {};
6197         e.stopEvent();
6198         this.fireEvent("beforeclose", this, o);
6199         if(o.cancel !== true){
6200             this.tabPanel.removeTab(this.id);
6201         }
6202     },
6203     /**
6204      * The text displayed in the tooltip for the close icon.
6205      * @type String
6206      */
6207     closeText : "Close this tab"
6208 });
6209
6210 /** @private */
6211 Roo.TabPanel.prototype.createStrip = function(container){
6212     var strip = document.createElement("div");
6213     strip.className = "x-tabs-wrap";
6214     container.appendChild(strip);
6215     return strip;
6216 };
6217 /** @private */
6218 Roo.TabPanel.prototype.createStripList = function(strip){
6219     // div wrapper for retard IE
6220     // returns the "tr" element.
6221     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6222         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6223         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6224     return strip.firstChild.firstChild.firstChild.firstChild;
6225 };
6226 /** @private */
6227 Roo.TabPanel.prototype.createBody = function(container){
6228     var body = document.createElement("div");
6229     Roo.id(body, "tab-body");
6230     Roo.fly(body).addClass("x-tabs-body");
6231     container.appendChild(body);
6232     return body;
6233 };
6234 /** @private */
6235 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6236     var body = Roo.getDom(id);
6237     if(!body){
6238         body = document.createElement("div");
6239         body.id = id;
6240     }
6241     Roo.fly(body).addClass("x-tabs-item-body");
6242     bodyEl.insertBefore(body, bodyEl.firstChild);
6243     return body;
6244 };
6245 /** @private */
6246 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6247     var td = document.createElement("td");
6248     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6249     //stripEl.appendChild(td);
6250     if(closable){
6251         td.className = "x-tabs-closable";
6252         if(!this.closeTpl){
6253             this.closeTpl = new Roo.Template(
6254                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6255                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6256                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6257             );
6258         }
6259         var el = this.closeTpl.overwrite(td, {"text": text});
6260         var close = el.getElementsByTagName("div")[0];
6261         var inner = el.getElementsByTagName("em")[0];
6262         return {"el": el, "close": close, "inner": inner};
6263     } else {
6264         if(!this.tabTpl){
6265             this.tabTpl = new Roo.Template(
6266                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6267                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6268             );
6269         }
6270         var el = this.tabTpl.overwrite(td, {"text": text});
6271         var inner = el.getElementsByTagName("em")[0];
6272         return {"el": el, "inner": inner};
6273     }
6274 };/*
6275  * Based on:
6276  * Ext JS Library 1.1.1
6277  * Copyright(c) 2006-2007, Ext JS, LLC.
6278  *
6279  * Originally Released Under LGPL - original licence link has changed is not relivant.
6280  *
6281  * Fork - LGPL
6282  * <script type="text/javascript">
6283  */
6284
6285 /**
6286  * @class Roo.Button
6287  * @extends Roo.util.Observable
6288  * Simple Button class
6289  * @cfg {String} text The button text
6290  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6291  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6292  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6293  * @cfg {Object} scope The scope of the handler
6294  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6295  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6296  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6297  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6298  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6299  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6300    applies if enableToggle = true)
6301  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6302  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6303   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6304  * @constructor
6305  * Create a new button
6306  * @param {Object} config The config object
6307  */
6308 Roo.Button = function(renderTo, config)
6309 {
6310     if (!config) {
6311         config = renderTo;
6312         renderTo = config.renderTo || false;
6313     }
6314     
6315     Roo.apply(this, config);
6316     this.addEvents({
6317         /**
6318              * @event click
6319              * Fires when this button is clicked
6320              * @param {Button} this
6321              * @param {EventObject} e The click event
6322              */
6323             "click" : true,
6324         /**
6325              * @event toggle
6326              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6327              * @param {Button} this
6328              * @param {Boolean} pressed
6329              */
6330             "toggle" : true,
6331         /**
6332              * @event mouseover
6333              * Fires when the mouse hovers over the button
6334              * @param {Button} this
6335              * @param {Event} e The event object
6336              */
6337         'mouseover' : true,
6338         /**
6339              * @event mouseout
6340              * Fires when the mouse exits the button
6341              * @param {Button} this
6342              * @param {Event} e The event object
6343              */
6344         'mouseout': true,
6345          /**
6346              * @event render
6347              * Fires when the button is rendered
6348              * @param {Button} this
6349              */
6350         'render': true
6351     });
6352     if(this.menu){
6353         this.menu = Roo.menu.MenuMgr.get(this.menu);
6354     }
6355     // register listeners first!!  - so render can be captured..
6356     Roo.util.Observable.call(this);
6357     if(renderTo){
6358         this.render(renderTo);
6359     }
6360     
6361   
6362 };
6363
6364 Roo.extend(Roo.Button, Roo.util.Observable, {
6365     /**
6366      * 
6367      */
6368     
6369     /**
6370      * Read-only. True if this button is hidden
6371      * @type Boolean
6372      */
6373     hidden : false,
6374     /**
6375      * Read-only. True if this button is disabled
6376      * @type Boolean
6377      */
6378     disabled : false,
6379     /**
6380      * Read-only. True if this button is pressed (only if enableToggle = true)
6381      * @type Boolean
6382      */
6383     pressed : false,
6384
6385     /**
6386      * @cfg {Number} tabIndex 
6387      * The DOM tabIndex for this button (defaults to undefined)
6388      */
6389     tabIndex : undefined,
6390
6391     /**
6392      * @cfg {Boolean} enableToggle
6393      * True to enable pressed/not pressed toggling (defaults to false)
6394      */
6395     enableToggle: false,
6396     /**
6397      * @cfg {Roo.menu.Menu} menu
6398      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6399      */
6400     menu : undefined,
6401     /**
6402      * @cfg {String} menuAlign
6403      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6404      */
6405     menuAlign : "tl-bl?",
6406
6407     /**
6408      * @cfg {String} iconCls
6409      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6410      */
6411     iconCls : undefined,
6412     /**
6413      * @cfg {String} type
6414      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6415      */
6416     type : 'button',
6417
6418     // private
6419     menuClassTarget: 'tr',
6420
6421     /**
6422      * @cfg {String} clickEvent
6423      * The type of event to map to the button's event handler (defaults to 'click')
6424      */
6425     clickEvent : 'click',
6426
6427     /**
6428      * @cfg {Boolean} handleMouseEvents
6429      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6430      */
6431     handleMouseEvents : true,
6432
6433     /**
6434      * @cfg {String} tooltipType
6435      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6436      */
6437     tooltipType : 'qtip',
6438
6439     /**
6440      * @cfg {String} cls
6441      * A CSS class to apply to the button's main element.
6442      */
6443     
6444     /**
6445      * @cfg {Roo.Template} template (Optional)
6446      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6447      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6448      * require code modifications if required elements (e.g. a button) aren't present.
6449      */
6450
6451     // private
6452     render : function(renderTo){
6453         var btn;
6454         if(this.hideParent){
6455             this.parentEl = Roo.get(renderTo);
6456         }
6457         if(!this.dhconfig){
6458             if(!this.template){
6459                 if(!Roo.Button.buttonTemplate){
6460                     // hideous table template
6461                     Roo.Button.buttonTemplate = new Roo.Template(
6462                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6463                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6464                         "</tr></tbody></table>");
6465                 }
6466                 this.template = Roo.Button.buttonTemplate;
6467             }
6468             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6469             var btnEl = btn.child("button:first");
6470             btnEl.on('focus', this.onFocus, this);
6471             btnEl.on('blur', this.onBlur, this);
6472             if(this.cls){
6473                 btn.addClass(this.cls);
6474             }
6475             if(this.icon){
6476                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6477             }
6478             if(this.iconCls){
6479                 btnEl.addClass(this.iconCls);
6480                 if(!this.cls){
6481                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6482                 }
6483             }
6484             if(this.tabIndex !== undefined){
6485                 btnEl.dom.tabIndex = this.tabIndex;
6486             }
6487             if(this.tooltip){
6488                 if(typeof this.tooltip == 'object'){
6489                     Roo.QuickTips.tips(Roo.apply({
6490                           target: btnEl.id
6491                     }, this.tooltip));
6492                 } else {
6493                     btnEl.dom[this.tooltipType] = this.tooltip;
6494                 }
6495             }
6496         }else{
6497             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6498         }
6499         this.el = btn;
6500         if(this.id){
6501             this.el.dom.id = this.el.id = this.id;
6502         }
6503         if(this.menu){
6504             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6505             this.menu.on("show", this.onMenuShow, this);
6506             this.menu.on("hide", this.onMenuHide, this);
6507         }
6508         btn.addClass("x-btn");
6509         if(Roo.isIE && !Roo.isIE7){
6510             this.autoWidth.defer(1, this);
6511         }else{
6512             this.autoWidth();
6513         }
6514         if(this.handleMouseEvents){
6515             btn.on("mouseover", this.onMouseOver, this);
6516             btn.on("mouseout", this.onMouseOut, this);
6517             btn.on("mousedown", this.onMouseDown, this);
6518         }
6519         btn.on(this.clickEvent, this.onClick, this);
6520         //btn.on("mouseup", this.onMouseUp, this);
6521         if(this.hidden){
6522             this.hide();
6523         }
6524         if(this.disabled){
6525             this.disable();
6526         }
6527         Roo.ButtonToggleMgr.register(this);
6528         if(this.pressed){
6529             this.el.addClass("x-btn-pressed");
6530         }
6531         if(this.repeat){
6532             var repeater = new Roo.util.ClickRepeater(btn,
6533                 typeof this.repeat == "object" ? this.repeat : {}
6534             );
6535             repeater.on("click", this.onClick,  this);
6536         }
6537         
6538         this.fireEvent('render', this);
6539         
6540     },
6541     /**
6542      * Returns the button's underlying element
6543      * @return {Roo.Element} The element
6544      */
6545     getEl : function(){
6546         return this.el;  
6547     },
6548     
6549     /**
6550      * Destroys this Button and removes any listeners.
6551      */
6552     destroy : function(){
6553         Roo.ButtonToggleMgr.unregister(this);
6554         this.el.removeAllListeners();
6555         this.purgeListeners();
6556         this.el.remove();
6557     },
6558
6559     // private
6560     autoWidth : function(){
6561         if(this.el){
6562             this.el.setWidth("auto");
6563             if(Roo.isIE7 && Roo.isStrict){
6564                 var ib = this.el.child('button');
6565                 if(ib && ib.getWidth() > 20){
6566                     ib.clip();
6567                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6568                 }
6569             }
6570             if(this.minWidth){
6571                 if(this.hidden){
6572                     this.el.beginMeasure();
6573                 }
6574                 if(this.el.getWidth() < this.minWidth){
6575                     this.el.setWidth(this.minWidth);
6576                 }
6577                 if(this.hidden){
6578                     this.el.endMeasure();
6579                 }
6580             }
6581         }
6582     },
6583
6584     /**
6585      * Assigns this button's click handler
6586      * @param {Function} handler The function to call when the button is clicked
6587      * @param {Object} scope (optional) Scope for the function passed in
6588      */
6589     setHandler : function(handler, scope){
6590         this.handler = handler;
6591         this.scope = scope;  
6592     },
6593     
6594     /**
6595      * Sets this button's text
6596      * @param {String} text The button text
6597      */
6598     setText : function(text){
6599         this.text = text;
6600         if(this.el){
6601             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6602         }
6603         this.autoWidth();
6604     },
6605     
6606     /**
6607      * Gets the text for this button
6608      * @return {String} The button text
6609      */
6610     getText : function(){
6611         return this.text;  
6612     },
6613     
6614     /**
6615      * Show this button
6616      */
6617     show: function(){
6618         this.hidden = false;
6619         if(this.el){
6620             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6621         }
6622     },
6623     
6624     /**
6625      * Hide this button
6626      */
6627     hide: function(){
6628         this.hidden = true;
6629         if(this.el){
6630             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6631         }
6632     },
6633     
6634     /**
6635      * Convenience function for boolean show/hide
6636      * @param {Boolean} visible True to show, false to hide
6637      */
6638     setVisible: function(visible){
6639         if(visible) {
6640             this.show();
6641         }else{
6642             this.hide();
6643         }
6644     },
6645     
6646     /**
6647      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6648      * @param {Boolean} state (optional) Force a particular state
6649      */
6650     toggle : function(state){
6651         state = state === undefined ? !this.pressed : state;
6652         if(state != this.pressed){
6653             if(state){
6654                 this.el.addClass("x-btn-pressed");
6655                 this.pressed = true;
6656                 this.fireEvent("toggle", this, true);
6657             }else{
6658                 this.el.removeClass("x-btn-pressed");
6659                 this.pressed = false;
6660                 this.fireEvent("toggle", this, false);
6661             }
6662             if(this.toggleHandler){
6663                 this.toggleHandler.call(this.scope || this, this, state);
6664             }
6665         }
6666     },
6667     
6668     /**
6669      * Focus the button
6670      */
6671     focus : function(){
6672         this.el.child('button:first').focus();
6673     },
6674     
6675     /**
6676      * Disable this button
6677      */
6678     disable : function(){
6679         if(this.el){
6680             this.el.addClass("x-btn-disabled");
6681         }
6682         this.disabled = true;
6683     },
6684     
6685     /**
6686      * Enable this button
6687      */
6688     enable : function(){
6689         if(this.el){
6690             this.el.removeClass("x-btn-disabled");
6691         }
6692         this.disabled = false;
6693     },
6694
6695     /**
6696      * Convenience function for boolean enable/disable
6697      * @param {Boolean} enabled True to enable, false to disable
6698      */
6699     setDisabled : function(v){
6700         this[v !== true ? "enable" : "disable"]();
6701     },
6702
6703     // private
6704     onClick : function(e)
6705     {
6706         if(e){
6707             e.preventDefault();
6708         }
6709         if(e.button != 0){
6710             return;
6711         }
6712         if(!this.disabled){
6713             if(this.enableToggle){
6714                 this.toggle();
6715             }
6716             if(this.menu && !this.menu.isVisible()){
6717                 this.menu.show(this.el, this.menuAlign);
6718             }
6719             this.fireEvent("click", this, e);
6720             if(this.handler){
6721                 this.el.removeClass("x-btn-over");
6722                 this.handler.call(this.scope || this, this, e);
6723             }
6724         }
6725     },
6726     // private
6727     onMouseOver : function(e){
6728         if(!this.disabled){
6729             this.el.addClass("x-btn-over");
6730             this.fireEvent('mouseover', this, e);
6731         }
6732     },
6733     // private
6734     onMouseOut : function(e){
6735         if(!e.within(this.el,  true)){
6736             this.el.removeClass("x-btn-over");
6737             this.fireEvent('mouseout', this, e);
6738         }
6739     },
6740     // private
6741     onFocus : function(e){
6742         if(!this.disabled){
6743             this.el.addClass("x-btn-focus");
6744         }
6745     },
6746     // private
6747     onBlur : function(e){
6748         this.el.removeClass("x-btn-focus");
6749     },
6750     // private
6751     onMouseDown : function(e){
6752         if(!this.disabled && e.button == 0){
6753             this.el.addClass("x-btn-click");
6754             Roo.get(document).on('mouseup', this.onMouseUp, this);
6755         }
6756     },
6757     // private
6758     onMouseUp : function(e){
6759         if(e.button == 0){
6760             this.el.removeClass("x-btn-click");
6761             Roo.get(document).un('mouseup', this.onMouseUp, this);
6762         }
6763     },
6764     // private
6765     onMenuShow : function(e){
6766         this.el.addClass("x-btn-menu-active");
6767     },
6768     // private
6769     onMenuHide : function(e){
6770         this.el.removeClass("x-btn-menu-active");
6771     }   
6772 });
6773
6774 // Private utility class used by Button
6775 Roo.ButtonToggleMgr = function(){
6776    var groups = {};
6777    
6778    function toggleGroup(btn, state){
6779        if(state){
6780            var g = groups[btn.toggleGroup];
6781            for(var i = 0, l = g.length; i < l; i++){
6782                if(g[i] != btn){
6783                    g[i].toggle(false);
6784                }
6785            }
6786        }
6787    }
6788    
6789    return {
6790        register : function(btn){
6791            if(!btn.toggleGroup){
6792                return;
6793            }
6794            var g = groups[btn.toggleGroup];
6795            if(!g){
6796                g = groups[btn.toggleGroup] = [];
6797            }
6798            g.push(btn);
6799            btn.on("toggle", toggleGroup);
6800        },
6801        
6802        unregister : function(btn){
6803            if(!btn.toggleGroup){
6804                return;
6805            }
6806            var g = groups[btn.toggleGroup];
6807            if(g){
6808                g.remove(btn);
6809                btn.un("toggle", toggleGroup);
6810            }
6811        }
6812    };
6813 }();/*
6814  * Based on:
6815  * Ext JS Library 1.1.1
6816  * Copyright(c) 2006-2007, Ext JS, LLC.
6817  *
6818  * Originally Released Under LGPL - original licence link has changed is not relivant.
6819  *
6820  * Fork - LGPL
6821  * <script type="text/javascript">
6822  */
6823  
6824 /**
6825  * @class Roo.SplitButton
6826  * @extends Roo.Button
6827  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6828  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6829  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6830  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6831  * @cfg {String} arrowTooltip The title attribute of the arrow
6832  * @constructor
6833  * Create a new menu button
6834  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6835  * @param {Object} config The config object
6836  */
6837 Roo.SplitButton = function(renderTo, config){
6838     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6839     /**
6840      * @event arrowclick
6841      * Fires when this button's arrow is clicked
6842      * @param {SplitButton} this
6843      * @param {EventObject} e The click event
6844      */
6845     this.addEvents({"arrowclick":true});
6846 };
6847
6848 Roo.extend(Roo.SplitButton, Roo.Button, {
6849     render : function(renderTo){
6850         // this is one sweet looking template!
6851         var tpl = new Roo.Template(
6852             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6853             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6854             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
6855             "</tbody></table></td><td>",
6856             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6857             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
6858             "</tbody></table></td></tr></table>"
6859         );
6860         var btn = tpl.append(renderTo, [this.text, this.type], true);
6861         var btnEl = btn.child("button");
6862         if(this.cls){
6863             btn.addClass(this.cls);
6864         }
6865         if(this.icon){
6866             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6867         }
6868         if(this.iconCls){
6869             btnEl.addClass(this.iconCls);
6870             if(!this.cls){
6871                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6872             }
6873         }
6874         this.el = btn;
6875         if(this.handleMouseEvents){
6876             btn.on("mouseover", this.onMouseOver, this);
6877             btn.on("mouseout", this.onMouseOut, this);
6878             btn.on("mousedown", this.onMouseDown, this);
6879             btn.on("mouseup", this.onMouseUp, this);
6880         }
6881         btn.on(this.clickEvent, this.onClick, this);
6882         if(this.tooltip){
6883             if(typeof this.tooltip == 'object'){
6884                 Roo.QuickTips.tips(Roo.apply({
6885                       target: btnEl.id
6886                 }, this.tooltip));
6887             } else {
6888                 btnEl.dom[this.tooltipType] = this.tooltip;
6889             }
6890         }
6891         if(this.arrowTooltip){
6892             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6893         }
6894         if(this.hidden){
6895             this.hide();
6896         }
6897         if(this.disabled){
6898             this.disable();
6899         }
6900         if(this.pressed){
6901             this.el.addClass("x-btn-pressed");
6902         }
6903         if(Roo.isIE && !Roo.isIE7){
6904             this.autoWidth.defer(1, this);
6905         }else{
6906             this.autoWidth();
6907         }
6908         if(this.menu){
6909             this.menu.on("show", this.onMenuShow, this);
6910             this.menu.on("hide", this.onMenuHide, this);
6911         }
6912         this.fireEvent('render', this);
6913     },
6914
6915     // private
6916     autoWidth : function(){
6917         if(this.el){
6918             var tbl = this.el.child("table:first");
6919             var tbl2 = this.el.child("table:last");
6920             this.el.setWidth("auto");
6921             tbl.setWidth("auto");
6922             if(Roo.isIE7 && Roo.isStrict){
6923                 var ib = this.el.child('button:first');
6924                 if(ib && ib.getWidth() > 20){
6925                     ib.clip();
6926                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6927                 }
6928             }
6929             if(this.minWidth){
6930                 if(this.hidden){
6931                     this.el.beginMeasure();
6932                 }
6933                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6934                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6935                 }
6936                 if(this.hidden){
6937                     this.el.endMeasure();
6938                 }
6939             }
6940             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6941         } 
6942     },
6943     /**
6944      * Sets this button's click handler
6945      * @param {Function} handler The function to call when the button is clicked
6946      * @param {Object} scope (optional) Scope for the function passed above
6947      */
6948     setHandler : function(handler, scope){
6949         this.handler = handler;
6950         this.scope = scope;  
6951     },
6952     
6953     /**
6954      * Sets this button's arrow click handler
6955      * @param {Function} handler The function to call when the arrow is clicked
6956      * @param {Object} scope (optional) Scope for the function passed above
6957      */
6958     setArrowHandler : function(handler, scope){
6959         this.arrowHandler = handler;
6960         this.scope = scope;  
6961     },
6962     
6963     /**
6964      * Focus the button
6965      */
6966     focus : function(){
6967         if(this.el){
6968             this.el.child("button:first").focus();
6969         }
6970     },
6971
6972     // private
6973     onClick : function(e){
6974         e.preventDefault();
6975         if(!this.disabled){
6976             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6977                 if(this.menu && !this.menu.isVisible()){
6978                     this.menu.show(this.el, this.menuAlign);
6979                 }
6980                 this.fireEvent("arrowclick", this, e);
6981                 if(this.arrowHandler){
6982                     this.arrowHandler.call(this.scope || this, this, e);
6983                 }
6984             }else{
6985                 this.fireEvent("click", this, e);
6986                 if(this.handler){
6987                     this.handler.call(this.scope || this, this, e);
6988                 }
6989             }
6990         }
6991     },
6992     // private
6993     onMouseDown : function(e){
6994         if(!this.disabled){
6995             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6996         }
6997     },
6998     // private
6999     onMouseUp : function(e){
7000         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7001     }   
7002 });
7003
7004
7005 // backwards compat
7006 Roo.MenuButton = Roo.SplitButton;/*
7007  * Based on:
7008  * Ext JS Library 1.1.1
7009  * Copyright(c) 2006-2007, Ext JS, LLC.
7010  *
7011  * Originally Released Under LGPL - original licence link has changed is not relivant.
7012  *
7013  * Fork - LGPL
7014  * <script type="text/javascript">
7015  */
7016
7017 /**
7018  * @class Roo.Toolbar
7019  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
7020  * Basic Toolbar class.
7021  * @constructor
7022  * Creates a new Toolbar
7023  * @param {Object} container The config object
7024  */ 
7025 Roo.Toolbar = function(container, buttons, config)
7026 {
7027     /// old consturctor format still supported..
7028     if(container instanceof Array){ // omit the container for later rendering
7029         buttons = container;
7030         config = buttons;
7031         container = null;
7032     }
7033     if (typeof(container) == 'object' && container.xtype) {
7034         config = container;
7035         container = config.container;
7036         buttons = config.buttons || []; // not really - use items!!
7037     }
7038     var xitems = [];
7039     if (config && config.items) {
7040         xitems = config.items;
7041         delete config.items;
7042     }
7043     Roo.apply(this, config);
7044     this.buttons = buttons;
7045     
7046     if(container){
7047         this.render(container);
7048     }
7049     this.xitems = xitems;
7050     Roo.each(xitems, function(b) {
7051         this.add(b);
7052     }, this);
7053     
7054 };
7055
7056 Roo.Toolbar.prototype = {
7057     /**
7058      * @cfg {Array} items
7059      * array of button configs or elements to add (will be converted to a MixedCollection)
7060      */
7061     items: false,
7062     /**
7063      * @cfg {String/HTMLElement/Element} container
7064      * The id or element that will contain the toolbar
7065      */
7066     // private
7067     render : function(ct){
7068         this.el = Roo.get(ct);
7069         if(this.cls){
7070             this.el.addClass(this.cls);
7071         }
7072         // using a table allows for vertical alignment
7073         // 100% width is needed by Safari...
7074         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7075         this.tr = this.el.child("tr", true);
7076         var autoId = 0;
7077         this.items = new Roo.util.MixedCollection(false, function(o){
7078             return o.id || ("item" + (++autoId));
7079         });
7080         if(this.buttons){
7081             this.add.apply(this, this.buttons);
7082             delete this.buttons;
7083         }
7084     },
7085
7086     /**
7087      * Adds element(s) to the toolbar -- this function takes a variable number of 
7088      * arguments of mixed type and adds them to the toolbar.
7089      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7090      * <ul>
7091      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7092      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7093      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7094      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7095      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7096      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7097      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7098      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7099      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7100      * </ul>
7101      * @param {Mixed} arg2
7102      * @param {Mixed} etc.
7103      */
7104     add : function(){
7105         var a = arguments, l = a.length;
7106         for(var i = 0; i < l; i++){
7107             this._add(a[i]);
7108         }
7109     },
7110     // private..
7111     _add : function(el) {
7112         
7113         if (el.xtype) {
7114             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7115         }
7116         
7117         if (el.applyTo){ // some kind of form field
7118             return this.addField(el);
7119         } 
7120         if (el.render){ // some kind of Toolbar.Item
7121             return this.addItem(el);
7122         }
7123         if (typeof el == "string"){ // string
7124             if(el == "separator" || el == "-"){
7125                 return this.addSeparator();
7126             }
7127             if (el == " "){
7128                 return this.addSpacer();
7129             }
7130             if(el == "->"){
7131                 return this.addFill();
7132             }
7133             return this.addText(el);
7134             
7135         }
7136         if(el.tagName){ // element
7137             return this.addElement(el);
7138         }
7139         if(typeof el == "object"){ // must be button config?
7140             return this.addButton(el);
7141         }
7142         // and now what?!?!
7143         return false;
7144         
7145     },
7146     
7147     /**
7148      * Add an Xtype element
7149      * @param {Object} xtype Xtype Object
7150      * @return {Object} created Object
7151      */
7152     addxtype : function(e){
7153         return this.add(e);  
7154     },
7155     
7156     /**
7157      * Returns the Element for this toolbar.
7158      * @return {Roo.Element}
7159      */
7160     getEl : function(){
7161         return this.el;  
7162     },
7163     
7164     /**
7165      * Adds a separator
7166      * @return {Roo.Toolbar.Item} The separator item
7167      */
7168     addSeparator : function(){
7169         return this.addItem(new Roo.Toolbar.Separator());
7170     },
7171
7172     /**
7173      * Adds a spacer element
7174      * @return {Roo.Toolbar.Spacer} The spacer item
7175      */
7176     addSpacer : function(){
7177         return this.addItem(new Roo.Toolbar.Spacer());
7178     },
7179
7180     /**
7181      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7182      * @return {Roo.Toolbar.Fill} The fill item
7183      */
7184     addFill : function(){
7185         return this.addItem(new Roo.Toolbar.Fill());
7186     },
7187
7188     /**
7189      * Adds any standard HTML element to the toolbar
7190      * @param {String/HTMLElement/Element} el The element or id of the element to add
7191      * @return {Roo.Toolbar.Item} The element's item
7192      */
7193     addElement : function(el){
7194         return this.addItem(new Roo.Toolbar.Item(el));
7195     },
7196     /**
7197      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7198      * @type Roo.util.MixedCollection  
7199      */
7200     items : false,
7201      
7202     /**
7203      * Adds any Toolbar.Item or subclass
7204      * @param {Roo.Toolbar.Item} item
7205      * @return {Roo.Toolbar.Item} The item
7206      */
7207     addItem : function(item){
7208         var td = this.nextBlock();
7209         item.render(td);
7210         this.items.add(item);
7211         return item;
7212     },
7213     
7214     /**
7215      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7216      * @param {Object/Array} config A button config or array of configs
7217      * @return {Roo.Toolbar.Button/Array}
7218      */
7219     addButton : function(config){
7220         if(config instanceof Array){
7221             var buttons = [];
7222             for(var i = 0, len = config.length; i < len; i++) {
7223                 buttons.push(this.addButton(config[i]));
7224             }
7225             return buttons;
7226         }
7227         var b = config;
7228         if(!(config instanceof Roo.Toolbar.Button)){
7229             b = config.split ?
7230                 new Roo.Toolbar.SplitButton(config) :
7231                 new Roo.Toolbar.Button(config);
7232         }
7233         var td = this.nextBlock();
7234         b.render(td);
7235         this.items.add(b);
7236         return b;
7237     },
7238     
7239     /**
7240      * Adds text to the toolbar
7241      * @param {String} text The text to add
7242      * @return {Roo.Toolbar.Item} The element's item
7243      */
7244     addText : function(text){
7245         return this.addItem(new Roo.Toolbar.TextItem(text));
7246     },
7247     
7248     /**
7249      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7250      * @param {Number} index The index where the item is to be inserted
7251      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7252      * @return {Roo.Toolbar.Button/Item}
7253      */
7254     insertButton : function(index, item){
7255         if(item instanceof Array){
7256             var buttons = [];
7257             for(var i = 0, len = item.length; i < len; i++) {
7258                buttons.push(this.insertButton(index + i, item[i]));
7259             }
7260             return buttons;
7261         }
7262         if (!(item instanceof Roo.Toolbar.Button)){
7263            item = new Roo.Toolbar.Button(item);
7264         }
7265         var td = document.createElement("td");
7266         this.tr.insertBefore(td, this.tr.childNodes[index]);
7267         item.render(td);
7268         this.items.insert(index, item);
7269         return item;
7270     },
7271     
7272     /**
7273      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7274      * @param {Object} config
7275      * @return {Roo.Toolbar.Item} The element's item
7276      */
7277     addDom : function(config, returnEl){
7278         var td = this.nextBlock();
7279         Roo.DomHelper.overwrite(td, config);
7280         var ti = new Roo.Toolbar.Item(td.firstChild);
7281         ti.render(td);
7282         this.items.add(ti);
7283         return ti;
7284     },
7285
7286     /**
7287      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7288      * @type Roo.util.MixedCollection  
7289      */
7290     fields : false,
7291     
7292     /**
7293      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7294      * Note: the field should not have been rendered yet. For a field that has already been
7295      * rendered, use {@link #addElement}.
7296      * @param {Roo.form.Field} field
7297      * @return {Roo.ToolbarItem}
7298      */
7299      
7300       
7301     addField : function(field) {
7302         if (!this.fields) {
7303             var autoId = 0;
7304             this.fields = new Roo.util.MixedCollection(false, function(o){
7305                 return o.id || ("item" + (++autoId));
7306             });
7307
7308         }
7309         
7310         var td = this.nextBlock();
7311         field.render(td);
7312         var ti = new Roo.Toolbar.Item(td.firstChild);
7313         ti.render(td);
7314         this.items.add(ti);
7315         this.fields.add(field);
7316         return ti;
7317     },
7318     /**
7319      * Hide the toolbar
7320      * @method hide
7321      */
7322      
7323       
7324     hide : function()
7325     {
7326         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7327         this.el.child('div').hide();
7328     },
7329     /**
7330      * Show the toolbar
7331      * @method show
7332      */
7333     show : function()
7334     {
7335         this.el.child('div').show();
7336     },
7337       
7338     // private
7339     nextBlock : function(){
7340         var td = document.createElement("td");
7341         this.tr.appendChild(td);
7342         return td;
7343     },
7344
7345     // private
7346     destroy : function(){
7347         if(this.items){ // rendered?
7348             Roo.destroy.apply(Roo, this.items.items);
7349         }
7350         if(this.fields){ // rendered?
7351             Roo.destroy.apply(Roo, this.fields.items);
7352         }
7353         Roo.Element.uncache(this.el, this.tr);
7354     }
7355 };
7356
7357 /**
7358  * @class Roo.Toolbar.Item
7359  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7360  * @constructor
7361  * Creates a new Item
7362  * @param {HTMLElement} el 
7363  */
7364 Roo.Toolbar.Item = function(el){
7365     var cfg = {};
7366     if (typeof (el.xtype) != 'undefined') {
7367         cfg = el;
7368         el = cfg.el;
7369     }
7370     
7371     this.el = Roo.getDom(el);
7372     this.id = Roo.id(this.el);
7373     this.hidden = false;
7374     
7375     this.addEvents({
7376          /**
7377              * @event render
7378              * Fires when the button is rendered
7379              * @param {Button} this
7380              */
7381         'render': true
7382     });
7383     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7384 };
7385 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7386 //Roo.Toolbar.Item.prototype = {
7387     
7388     /**
7389      * Get this item's HTML Element
7390      * @return {HTMLElement}
7391      */
7392     getEl : function(){
7393        return this.el;  
7394     },
7395
7396     // private
7397     render : function(td){
7398         
7399          this.td = td;
7400         td.appendChild(this.el);
7401         
7402         this.fireEvent('render', this);
7403     },
7404     
7405     /**
7406      * Removes and destroys this item.
7407      */
7408     destroy : function(){
7409         this.td.parentNode.removeChild(this.td);
7410     },
7411     
7412     /**
7413      * Shows this item.
7414      */
7415     show: function(){
7416         this.hidden = false;
7417         this.td.style.display = "";
7418     },
7419     
7420     /**
7421      * Hides this item.
7422      */
7423     hide: function(){
7424         this.hidden = true;
7425         this.td.style.display = "none";
7426     },
7427     
7428     /**
7429      * Convenience function for boolean show/hide.
7430      * @param {Boolean} visible true to show/false to hide
7431      */
7432     setVisible: function(visible){
7433         if(visible) {
7434             this.show();
7435         }else{
7436             this.hide();
7437         }
7438     },
7439     
7440     /**
7441      * Try to focus this item.
7442      */
7443     focus : function(){
7444         Roo.fly(this.el).focus();
7445     },
7446     
7447     /**
7448      * Disables this item.
7449      */
7450     disable : function(){
7451         Roo.fly(this.td).addClass("x-item-disabled");
7452         this.disabled = true;
7453         this.el.disabled = true;
7454     },
7455     
7456     /**
7457      * Enables this item.
7458      */
7459     enable : function(){
7460         Roo.fly(this.td).removeClass("x-item-disabled");
7461         this.disabled = false;
7462         this.el.disabled = false;
7463     }
7464 });
7465
7466
7467 /**
7468  * @class Roo.Toolbar.Separator
7469  * @extends Roo.Toolbar.Item
7470  * A simple toolbar separator class
7471  * @constructor
7472  * Creates a new Separator
7473  */
7474 Roo.Toolbar.Separator = function(cfg){
7475     
7476     var s = document.createElement("span");
7477     s.className = "ytb-sep";
7478     if (cfg) {
7479         cfg.el = s;
7480     }
7481     
7482     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7483 };
7484 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7485     enable:Roo.emptyFn,
7486     disable:Roo.emptyFn,
7487     focus:Roo.emptyFn
7488 });
7489
7490 /**
7491  * @class Roo.Toolbar.Spacer
7492  * @extends Roo.Toolbar.Item
7493  * A simple element that adds extra horizontal space to a toolbar.
7494  * @constructor
7495  * Creates a new Spacer
7496  */
7497 Roo.Toolbar.Spacer = function(cfg){
7498     var s = document.createElement("div");
7499     s.className = "ytb-spacer";
7500     if (cfg) {
7501         cfg.el = s;
7502     }
7503     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7504 };
7505 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7506     enable:Roo.emptyFn,
7507     disable:Roo.emptyFn,
7508     focus:Roo.emptyFn
7509 });
7510
7511 /**
7512  * @class Roo.Toolbar.Fill
7513  * @extends Roo.Toolbar.Spacer
7514  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7515  * @constructor
7516  * Creates a new Spacer
7517  */
7518 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7519     // private
7520     render : function(td){
7521         td.style.width = '100%';
7522         Roo.Toolbar.Fill.superclass.render.call(this, td);
7523     }
7524 });
7525
7526 /**
7527  * @class Roo.Toolbar.TextItem
7528  * @extends Roo.Toolbar.Item
7529  * A simple class that renders text directly into a toolbar.
7530  * @constructor
7531  * Creates a new TextItem
7532  * @cfg {string} text 
7533  */
7534 Roo.Toolbar.TextItem = function(cfg){
7535     var  text = cfg || "";
7536     if (typeof(cfg) == 'object') {
7537         text = cfg.text || "";
7538     }  else {
7539         cfg = null;
7540     }
7541     var s = document.createElement("span");
7542     s.className = "ytb-text";
7543     s.innerHTML = text;
7544     if (cfg) {
7545         cfg.el  = s;
7546     }
7547     
7548     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7549 };
7550 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7551     
7552      
7553     enable:Roo.emptyFn,
7554     disable:Roo.emptyFn,
7555     focus:Roo.emptyFn,
7556      /**
7557      * Shows this button
7558      */
7559     show: function(){
7560         this.hidden = false;
7561         this.el.style.display = "";
7562     },
7563     
7564     /**
7565      * Hides this button
7566      */
7567     hide: function(){
7568         this.hidden = true;
7569         this.el.style.display = "none";
7570     }
7571     
7572 });
7573
7574 /**
7575  * @class Roo.Toolbar.Button
7576  * @extends Roo.Button
7577  * A button that renders into a toolbar.
7578  * @constructor
7579  * Creates a new Button
7580  * @param {Object} config A standard {@link Roo.Button} config object
7581  */
7582 Roo.Toolbar.Button = function(config){
7583     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7584 };
7585 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7586 {
7587     
7588     
7589     render : function(td){
7590         this.td = td;
7591         Roo.Toolbar.Button.superclass.render.call(this, td);
7592     },
7593     
7594     /**
7595      * Removes and destroys this button
7596      */
7597     destroy : function(){
7598         Roo.Toolbar.Button.superclass.destroy.call(this);
7599         this.td.parentNode.removeChild(this.td);
7600     },
7601     
7602     /**
7603      * Shows this button
7604      */
7605     show: function(){
7606         this.hidden = false;
7607         this.td.style.display = "";
7608     },
7609     
7610     /**
7611      * Hides this button
7612      */
7613     hide: function(){
7614         this.hidden = true;
7615         this.td.style.display = "none";
7616     },
7617
7618     /**
7619      * Disables this item
7620      */
7621     disable : function(){
7622         Roo.fly(this.td).addClass("x-item-disabled");
7623         this.disabled = true;
7624     },
7625
7626     /**
7627      * Enables this item
7628      */
7629     enable : function(){
7630         Roo.fly(this.td).removeClass("x-item-disabled");
7631         this.disabled = false;
7632     }
7633 });
7634 // backwards compat
7635 Roo.ToolbarButton = Roo.Toolbar.Button;
7636
7637 /**
7638  * @class Roo.Toolbar.SplitButton
7639  * @extends Roo.SplitButton
7640  * A menu button that renders into a toolbar.
7641  * @constructor
7642  * Creates a new SplitButton
7643  * @param {Object} config A standard {@link Roo.SplitButton} config object
7644  */
7645 Roo.Toolbar.SplitButton = function(config){
7646     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7647 };
7648 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7649     render : function(td){
7650         this.td = td;
7651         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7652     },
7653     
7654     /**
7655      * Removes and destroys this button
7656      */
7657     destroy : function(){
7658         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7659         this.td.parentNode.removeChild(this.td);
7660     },
7661     
7662     /**
7663      * Shows this button
7664      */
7665     show: function(){
7666         this.hidden = false;
7667         this.td.style.display = "";
7668     },
7669     
7670     /**
7671      * Hides this button
7672      */
7673     hide: function(){
7674         this.hidden = true;
7675         this.td.style.display = "none";
7676     }
7677 });
7678
7679 // backwards compat
7680 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7681  * Based on:
7682  * Ext JS Library 1.1.1
7683  * Copyright(c) 2006-2007, Ext JS, LLC.
7684  *
7685  * Originally Released Under LGPL - original licence link has changed is not relivant.
7686  *
7687  * Fork - LGPL
7688  * <script type="text/javascript">
7689  */
7690  
7691 /**
7692  * @class Roo.PagingToolbar
7693  * @extends Roo.Toolbar
7694  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7695  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7696  * @constructor
7697  * Create a new PagingToolbar
7698  * @param {Object} config The config object
7699  */
7700 Roo.PagingToolbar = function(el, ds, config)
7701 {
7702     // old args format still supported... - xtype is prefered..
7703     if (typeof(el) == 'object' && el.xtype) {
7704         // created from xtype...
7705         config = el;
7706         ds = el.dataSource;
7707         el = config.container;
7708     }
7709     var items = [];
7710     if (config.items) {
7711         items = config.items;
7712         config.items = [];
7713     }
7714     
7715     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7716     this.ds = ds;
7717     this.cursor = 0;
7718     this.renderButtons(this.el);
7719     this.bind(ds);
7720     
7721     // supprot items array.
7722    
7723     Roo.each(items, function(e) {
7724         this.add(Roo.factory(e));
7725     },this);
7726     
7727 };
7728
7729 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7730    
7731     /**
7732      * @cfg {String/HTMLElement/Element} container
7733      * container The id or element that will contain the toolbar
7734      */
7735     /**
7736      * @cfg {Boolean} displayInfo
7737      * True to display the displayMsg (defaults to false)
7738      */
7739     
7740     
7741     /**
7742      * @cfg {Number} pageSize
7743      * The number of records to display per page (defaults to 20)
7744      */
7745     pageSize: 20,
7746     /**
7747      * @cfg {String} displayMsg
7748      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7749      */
7750     displayMsg : 'Displaying {0} - {1} of {2}',
7751     /**
7752      * @cfg {String} emptyMsg
7753      * The message to display when no records are found (defaults to "No data to display")
7754      */
7755     emptyMsg : 'No data to display',
7756     /**
7757      * Customizable piece of the default paging text (defaults to "Page")
7758      * @type String
7759      */
7760     beforePageText : "Page",
7761     /**
7762      * Customizable piece of the default paging text (defaults to "of %0")
7763      * @type String
7764      */
7765     afterPageText : "of {0}",
7766     /**
7767      * Customizable piece of the default paging text (defaults to "First Page")
7768      * @type String
7769      */
7770     firstText : "First Page",
7771     /**
7772      * Customizable piece of the default paging text (defaults to "Previous Page")
7773      * @type String
7774      */
7775     prevText : "Previous Page",
7776     /**
7777      * Customizable piece of the default paging text (defaults to "Next Page")
7778      * @type String
7779      */
7780     nextText : "Next Page",
7781     /**
7782      * Customizable piece of the default paging text (defaults to "Last Page")
7783      * @type String
7784      */
7785     lastText : "Last Page",
7786     /**
7787      * Customizable piece of the default paging text (defaults to "Refresh")
7788      * @type String
7789      */
7790     refreshText : "Refresh",
7791
7792     // private
7793     renderButtons : function(el){
7794         Roo.PagingToolbar.superclass.render.call(this, el);
7795         this.first = this.addButton({
7796             tooltip: this.firstText,
7797             cls: "x-btn-icon x-grid-page-first",
7798             disabled: true,
7799             handler: this.onClick.createDelegate(this, ["first"])
7800         });
7801         this.prev = this.addButton({
7802             tooltip: this.prevText,
7803             cls: "x-btn-icon x-grid-page-prev",
7804             disabled: true,
7805             handler: this.onClick.createDelegate(this, ["prev"])
7806         });
7807         //this.addSeparator();
7808         this.add(this.beforePageText);
7809         this.field = Roo.get(this.addDom({
7810            tag: "input",
7811            type: "text",
7812            size: "3",
7813            value: "1",
7814            cls: "x-grid-page-number"
7815         }).el);
7816         this.field.on("keydown", this.onPagingKeydown, this);
7817         this.field.on("focus", function(){this.dom.select();});
7818         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7819         this.field.setHeight(18);
7820         //this.addSeparator();
7821         this.next = this.addButton({
7822             tooltip: this.nextText,
7823             cls: "x-btn-icon x-grid-page-next",
7824             disabled: true,
7825             handler: this.onClick.createDelegate(this, ["next"])
7826         });
7827         this.last = this.addButton({
7828             tooltip: this.lastText,
7829             cls: "x-btn-icon x-grid-page-last",
7830             disabled: true,
7831             handler: this.onClick.createDelegate(this, ["last"])
7832         });
7833         //this.addSeparator();
7834         this.loading = this.addButton({
7835             tooltip: this.refreshText,
7836             cls: "x-btn-icon x-grid-loading",
7837             handler: this.onClick.createDelegate(this, ["refresh"])
7838         });
7839
7840         if(this.displayInfo){
7841             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7842         }
7843     },
7844
7845     // private
7846     updateInfo : function(){
7847         if(this.displayEl){
7848             var count = this.ds.getCount();
7849             var msg = count == 0 ?
7850                 this.emptyMsg :
7851                 String.format(
7852                     this.displayMsg,
7853                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7854                 );
7855             this.displayEl.update(msg);
7856         }
7857     },
7858
7859     // private
7860     onLoad : function(ds, r, o){
7861        this.cursor = o.params ? o.params.start : 0;
7862        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7863
7864        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7865        this.field.dom.value = ap;
7866        this.first.setDisabled(ap == 1);
7867        this.prev.setDisabled(ap == 1);
7868        this.next.setDisabled(ap == ps);
7869        this.last.setDisabled(ap == ps);
7870        this.loading.enable();
7871        this.updateInfo();
7872     },
7873
7874     // private
7875     getPageData : function(){
7876         var total = this.ds.getTotalCount();
7877         return {
7878             total : total,
7879             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7880             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7881         };
7882     },
7883
7884     // private
7885     onLoadError : function(){
7886         this.loading.enable();
7887     },
7888
7889     // private
7890     onPagingKeydown : function(e){
7891         var k = e.getKey();
7892         var d = this.getPageData();
7893         if(k == e.RETURN){
7894             var v = this.field.dom.value, pageNum;
7895             if(!v || isNaN(pageNum = parseInt(v, 10))){
7896                 this.field.dom.value = d.activePage;
7897                 return;
7898             }
7899             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7900             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7901             e.stopEvent();
7902         }
7903         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
7904         {
7905           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7906           this.field.dom.value = pageNum;
7907           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7908           e.stopEvent();
7909         }
7910         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7911         {
7912           var v = this.field.dom.value, pageNum; 
7913           var increment = (e.shiftKey) ? 10 : 1;
7914           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7915             increment *= -1;
7916           }
7917           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7918             this.field.dom.value = d.activePage;
7919             return;
7920           }
7921           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7922           {
7923             this.field.dom.value = parseInt(v, 10) + increment;
7924             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7925             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7926           }
7927           e.stopEvent();
7928         }
7929     },
7930
7931     // private
7932     beforeLoad : function(){
7933         if(this.loading){
7934             this.loading.disable();
7935         }
7936     },
7937     /**
7938      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
7939      * @param {String} which (first|prev|next|last|refresh)  which button to press.
7940      *
7941      */
7942     // private
7943     onClick : function(which){
7944         var ds = this.ds;
7945         switch(which){
7946             case "first":
7947                 ds.load({params:{start: 0, limit: this.pageSize}});
7948             break;
7949             case "prev":
7950                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7951             break;
7952             case "next":
7953                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7954             break;
7955             case "last":
7956                 var total = ds.getTotalCount();
7957                 var extra = total % this.pageSize;
7958                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7959                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7960             break;
7961             case "refresh":
7962                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7963             break;
7964         }
7965     },
7966
7967     /**
7968      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7969      * @param {Roo.data.Store} store The data store to unbind
7970      */
7971     unbind : function(ds){
7972         ds.un("beforeload", this.beforeLoad, this);
7973         ds.un("load", this.onLoad, this);
7974         ds.un("loadexception", this.onLoadError, this);
7975         ds.un("remove", this.updateInfo, this);
7976         ds.un("add", this.updateInfo, this);
7977         this.ds = undefined;
7978     },
7979
7980     /**
7981      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7982      * @param {Roo.data.Store} store The data store to bind
7983      */
7984     bind : function(ds){
7985         ds.on("beforeload", this.beforeLoad, this);
7986         ds.on("load", this.onLoad, this);
7987         ds.on("loadexception", this.onLoadError, this);
7988         ds.on("remove", this.updateInfo, this);
7989         ds.on("add", this.updateInfo, this);
7990         this.ds = ds;
7991     }
7992 });/*
7993  * Based on:
7994  * Ext JS Library 1.1.1
7995  * Copyright(c) 2006-2007, Ext JS, LLC.
7996  *
7997  * Originally Released Under LGPL - original licence link has changed is not relivant.
7998  *
7999  * Fork - LGPL
8000  * <script type="text/javascript">
8001  */
8002
8003 /**
8004  * @class Roo.Resizable
8005  * @extends Roo.util.Observable
8006  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8007  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8008  * 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
8009  * the element will be wrapped for you automatically.</p>
8010  * <p>Here is the list of valid resize handles:</p>
8011  * <pre>
8012 Value   Description
8013 ------  -------------------
8014  'n'     north
8015  's'     south
8016  'e'     east
8017  'w'     west
8018  'nw'    northwest
8019  'sw'    southwest
8020  'se'    southeast
8021  'ne'    northeast
8022  'hd'    horizontal drag
8023  'all'   all
8024 </pre>
8025  * <p>Here's an example showing the creation of a typical Resizable:</p>
8026  * <pre><code>
8027 var resizer = new Roo.Resizable("element-id", {
8028     handles: 'all',
8029     minWidth: 200,
8030     minHeight: 100,
8031     maxWidth: 500,
8032     maxHeight: 400,
8033     pinned: true
8034 });
8035 resizer.on("resize", myHandler);
8036 </code></pre>
8037  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8038  * resizer.east.setDisplayed(false);</p>
8039  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8040  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8041  * resize operation's new size (defaults to [0, 0])
8042  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8043  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8044  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8045  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8046  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8047  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8048  * @cfg {Number} width The width of the element in pixels (defaults to null)
8049  * @cfg {Number} height The height of the element in pixels (defaults to null)
8050  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8051  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8052  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8053  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8054  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8055  * in favor of the handles config option (defaults to false)
8056  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8057  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8058  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8059  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8060  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8061  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8062  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8063  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8064  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8065  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8066  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8067  * @constructor
8068  * Create a new resizable component
8069  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8070  * @param {Object} config configuration options
8071   */
8072 Roo.Resizable = function(el, config)
8073 {
8074     this.el = Roo.get(el);
8075
8076     if(config && config.wrap){
8077         config.resizeChild = this.el;
8078         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8079         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8080         this.el.setStyle("overflow", "hidden");
8081         this.el.setPositioning(config.resizeChild.getPositioning());
8082         config.resizeChild.clearPositioning();
8083         if(!config.width || !config.height){
8084             var csize = config.resizeChild.getSize();
8085             this.el.setSize(csize.width, csize.height);
8086         }
8087         if(config.pinned && !config.adjustments){
8088             config.adjustments = "auto";
8089         }
8090     }
8091
8092     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8093     this.proxy.unselectable();
8094     this.proxy.enableDisplayMode('block');
8095
8096     Roo.apply(this, config);
8097
8098     if(this.pinned){
8099         this.disableTrackOver = true;
8100         this.el.addClass("x-resizable-pinned");
8101     }
8102     // if the element isn't positioned, make it relative
8103     var position = this.el.getStyle("position");
8104     if(position != "absolute" && position != "fixed"){
8105         this.el.setStyle("position", "relative");
8106     }
8107     if(!this.handles){ // no handles passed, must be legacy style
8108         this.handles = 's,e,se';
8109         if(this.multiDirectional){
8110             this.handles += ',n,w';
8111         }
8112     }
8113     if(this.handles == "all"){
8114         this.handles = "n s e w ne nw se sw";
8115     }
8116     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8117     var ps = Roo.Resizable.positions;
8118     for(var i = 0, len = hs.length; i < len; i++){
8119         if(hs[i] && ps[hs[i]]){
8120             var pos = ps[hs[i]];
8121             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8122         }
8123     }
8124     // legacy
8125     this.corner = this.southeast;
8126     
8127     // updateBox = the box can move..
8128     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8129         this.updateBox = true;
8130     }
8131
8132     this.activeHandle = null;
8133
8134     if(this.resizeChild){
8135         if(typeof this.resizeChild == "boolean"){
8136             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8137         }else{
8138             this.resizeChild = Roo.get(this.resizeChild, true);
8139         }
8140     }
8141     
8142     if(this.adjustments == "auto"){
8143         var rc = this.resizeChild;
8144         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8145         if(rc && (hw || hn)){
8146             rc.position("relative");
8147             rc.setLeft(hw ? hw.el.getWidth() : 0);
8148             rc.setTop(hn ? hn.el.getHeight() : 0);
8149         }
8150         this.adjustments = [
8151             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8152             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8153         ];
8154     }
8155
8156     if(this.draggable){
8157         this.dd = this.dynamic ?
8158             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8159         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8160     }
8161
8162     // public events
8163     this.addEvents({
8164         /**
8165          * @event beforeresize
8166          * Fired before resize is allowed. Set enabled to false to cancel resize.
8167          * @param {Roo.Resizable} this
8168          * @param {Roo.EventObject} e The mousedown event
8169          */
8170         "beforeresize" : true,
8171         /**
8172          * @event resizing
8173          * Fired a resizing.
8174          * @param {Roo.Resizable} this
8175          * @param {Number} x The new x position
8176          * @param {Number} y The new y position
8177          * @param {Number} w The new w width
8178          * @param {Number} h The new h hight
8179          * @param {Roo.EventObject} e The mouseup event
8180          */
8181         "resizing" : true,
8182         /**
8183          * @event resize
8184          * Fired after a resize.
8185          * @param {Roo.Resizable} this
8186          * @param {Number} width The new width
8187          * @param {Number} height The new height
8188          * @param {Roo.EventObject} e The mouseup event
8189          */
8190         "resize" : true
8191     });
8192
8193     if(this.width !== null && this.height !== null){
8194         this.resizeTo(this.width, this.height);
8195     }else{
8196         this.updateChildSize();
8197     }
8198     if(Roo.isIE){
8199         this.el.dom.style.zoom = 1;
8200     }
8201     Roo.Resizable.superclass.constructor.call(this);
8202 };
8203
8204 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8205         resizeChild : false,
8206         adjustments : [0, 0],
8207         minWidth : 5,
8208         minHeight : 5,
8209         maxWidth : 10000,
8210         maxHeight : 10000,
8211         enabled : true,
8212         animate : false,
8213         duration : .35,
8214         dynamic : false,
8215         handles : false,
8216         multiDirectional : false,
8217         disableTrackOver : false,
8218         easing : 'easeOutStrong',
8219         widthIncrement : 0,
8220         heightIncrement : 0,
8221         pinned : false,
8222         width : null,
8223         height : null,
8224         preserveRatio : false,
8225         transparent: false,
8226         minX: 0,
8227         minY: 0,
8228         draggable: false,
8229
8230         /**
8231          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8232          */
8233         constrainTo: undefined,
8234         /**
8235          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8236          */
8237         resizeRegion: undefined,
8238
8239
8240     /**
8241      * Perform a manual resize
8242      * @param {Number} width
8243      * @param {Number} height
8244      */
8245     resizeTo : function(width, height){
8246         this.el.setSize(width, height);
8247         this.updateChildSize();
8248         this.fireEvent("resize", this, width, height, null);
8249     },
8250
8251     // private
8252     startSizing : function(e, handle){
8253         this.fireEvent("beforeresize", this, e);
8254         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8255
8256             if(!this.overlay){
8257                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8258                 this.overlay.unselectable();
8259                 this.overlay.enableDisplayMode("block");
8260                 this.overlay.on("mousemove", this.onMouseMove, this);
8261                 this.overlay.on("mouseup", this.onMouseUp, this);
8262             }
8263             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8264
8265             this.resizing = true;
8266             this.startBox = this.el.getBox();
8267             this.startPoint = e.getXY();
8268             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8269                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8270
8271             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8272             this.overlay.show();
8273
8274             if(this.constrainTo) {
8275                 var ct = Roo.get(this.constrainTo);
8276                 this.resizeRegion = ct.getRegion().adjust(
8277                     ct.getFrameWidth('t'),
8278                     ct.getFrameWidth('l'),
8279                     -ct.getFrameWidth('b'),
8280                     -ct.getFrameWidth('r')
8281                 );
8282             }
8283
8284             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8285             this.proxy.show();
8286             this.proxy.setBox(this.startBox);
8287             if(!this.dynamic){
8288                 this.proxy.setStyle('visibility', 'visible');
8289             }
8290         }
8291     },
8292
8293     // private
8294     onMouseDown : function(handle, e){
8295         if(this.enabled){
8296             e.stopEvent();
8297             this.activeHandle = handle;
8298             this.startSizing(e, handle);
8299         }
8300     },
8301
8302     // private
8303     onMouseUp : function(e){
8304         var size = this.resizeElement();
8305         this.resizing = false;
8306         this.handleOut();
8307         this.overlay.hide();
8308         this.proxy.hide();
8309         this.fireEvent("resize", this, size.width, size.height, e);
8310     },
8311
8312     // private
8313     updateChildSize : function(){
8314         
8315         if(this.resizeChild){
8316             var el = this.el;
8317             var child = this.resizeChild;
8318             var adj = this.adjustments;
8319             if(el.dom.offsetWidth){
8320                 var b = el.getSize(true);
8321                 child.setSize(b.width+adj[0], b.height+adj[1]);
8322             }
8323             // Second call here for IE
8324             // The first call enables instant resizing and
8325             // the second call corrects scroll bars if they
8326             // exist
8327             if(Roo.isIE){
8328                 setTimeout(function(){
8329                     if(el.dom.offsetWidth){
8330                         var b = el.getSize(true);
8331                         child.setSize(b.width+adj[0], b.height+adj[1]);
8332                     }
8333                 }, 10);
8334             }
8335         }
8336     },
8337
8338     // private
8339     snap : function(value, inc, min){
8340         if(!inc || !value) {
8341             return value;
8342         }
8343         var newValue = value;
8344         var m = value % inc;
8345         if(m > 0){
8346             if(m > (inc/2)){
8347                 newValue = value + (inc-m);
8348             }else{
8349                 newValue = value - m;
8350             }
8351         }
8352         return Math.max(min, newValue);
8353     },
8354
8355     // private
8356     resizeElement : function(){
8357         var box = this.proxy.getBox();
8358         if(this.updateBox){
8359             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8360         }else{
8361             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8362         }
8363         this.updateChildSize();
8364         if(!this.dynamic){
8365             this.proxy.hide();
8366         }
8367         return box;
8368     },
8369
8370     // private
8371     constrain : function(v, diff, m, mx){
8372         if(v - diff < m){
8373             diff = v - m;
8374         }else if(v - diff > mx){
8375             diff = mx - v;
8376         }
8377         return diff;
8378     },
8379
8380     // private
8381     onMouseMove : function(e){
8382         
8383         if(this.enabled){
8384             try{// try catch so if something goes wrong the user doesn't get hung
8385
8386             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8387                 return;
8388             }
8389
8390             //var curXY = this.startPoint;
8391             var curSize = this.curSize || this.startBox;
8392             var x = this.startBox.x, y = this.startBox.y;
8393             var ox = x, oy = y;
8394             var w = curSize.width, h = curSize.height;
8395             var ow = w, oh = h;
8396             var mw = this.minWidth, mh = this.minHeight;
8397             var mxw = this.maxWidth, mxh = this.maxHeight;
8398             var wi = this.widthIncrement;
8399             var hi = this.heightIncrement;
8400
8401             var eventXY = e.getXY();
8402             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8403             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8404
8405             var pos = this.activeHandle.position;
8406
8407             switch(pos){
8408                 case "east":
8409                     w += diffX;
8410                     w = Math.min(Math.max(mw, w), mxw);
8411                     break;
8412              
8413                 case "south":
8414                     h += diffY;
8415                     h = Math.min(Math.max(mh, h), mxh);
8416                     break;
8417                 case "southeast":
8418                     w += diffX;
8419                     h += diffY;
8420                     w = Math.min(Math.max(mw, w), mxw);
8421                     h = Math.min(Math.max(mh, h), mxh);
8422                     break;
8423                 case "north":
8424                     diffY = this.constrain(h, diffY, mh, mxh);
8425                     y += diffY;
8426                     h -= diffY;
8427                     break;
8428                 case "hdrag":
8429                     
8430                     if (wi) {
8431                         var adiffX = Math.abs(diffX);
8432                         var sub = (adiffX % wi); // how much 
8433                         if (sub > (wi/2)) { // far enough to snap
8434                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8435                         } else {
8436                             // remove difference.. 
8437                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8438                         }
8439                     }
8440                     x += diffX;
8441                     x = Math.max(this.minX, x);
8442                     break;
8443                 case "west":
8444                     diffX = this.constrain(w, diffX, mw, mxw);
8445                     x += diffX;
8446                     w -= diffX;
8447                     break;
8448                 case "northeast":
8449                     w += diffX;
8450                     w = Math.min(Math.max(mw, w), mxw);
8451                     diffY = this.constrain(h, diffY, mh, mxh);
8452                     y += diffY;
8453                     h -= diffY;
8454                     break;
8455                 case "northwest":
8456                     diffX = this.constrain(w, diffX, mw, mxw);
8457                     diffY = this.constrain(h, diffY, mh, mxh);
8458                     y += diffY;
8459                     h -= diffY;
8460                     x += diffX;
8461                     w -= diffX;
8462                     break;
8463                case "southwest":
8464                     diffX = this.constrain(w, diffX, mw, mxw);
8465                     h += diffY;
8466                     h = Math.min(Math.max(mh, h), mxh);
8467                     x += diffX;
8468                     w -= diffX;
8469                     break;
8470             }
8471
8472             var sw = this.snap(w, wi, mw);
8473             var sh = this.snap(h, hi, mh);
8474             if(sw != w || sh != h){
8475                 switch(pos){
8476                     case "northeast":
8477                         y -= sh - h;
8478                     break;
8479                     case "north":
8480                         y -= sh - h;
8481                         break;
8482                     case "southwest":
8483                         x -= sw - w;
8484                     break;
8485                     case "west":
8486                         x -= sw - w;
8487                         break;
8488                     case "northwest":
8489                         x -= sw - w;
8490                         y -= sh - h;
8491                     break;
8492                 }
8493                 w = sw;
8494                 h = sh;
8495             }
8496
8497             if(this.preserveRatio){
8498                 switch(pos){
8499                     case "southeast":
8500                     case "east":
8501                         h = oh * (w/ow);
8502                         h = Math.min(Math.max(mh, h), mxh);
8503                         w = ow * (h/oh);
8504                        break;
8505                     case "south":
8506                         w = ow * (h/oh);
8507                         w = Math.min(Math.max(mw, w), mxw);
8508                         h = oh * (w/ow);
8509                         break;
8510                     case "northeast":
8511                         w = ow * (h/oh);
8512                         w = Math.min(Math.max(mw, w), mxw);
8513                         h = oh * (w/ow);
8514                     break;
8515                     case "north":
8516                         var tw = w;
8517                         w = ow * (h/oh);
8518                         w = Math.min(Math.max(mw, w), mxw);
8519                         h = oh * (w/ow);
8520                         x += (tw - w) / 2;
8521                         break;
8522                     case "southwest":
8523                         h = oh * (w/ow);
8524                         h = Math.min(Math.max(mh, h), mxh);
8525                         var tw = w;
8526                         w = ow * (h/oh);
8527                         x += tw - w;
8528                         break;
8529                     case "west":
8530                         var th = h;
8531                         h = oh * (w/ow);
8532                         h = Math.min(Math.max(mh, h), mxh);
8533                         y += (th - h) / 2;
8534                         var tw = w;
8535                         w = ow * (h/oh);
8536                         x += tw - w;
8537                        break;
8538                     case "northwest":
8539                         var tw = w;
8540                         var th = h;
8541                         h = oh * (w/ow);
8542                         h = Math.min(Math.max(mh, h), mxh);
8543                         w = ow * (h/oh);
8544                         y += th - h;
8545                         x += tw - w;
8546                        break;
8547
8548                 }
8549             }
8550             if (pos == 'hdrag') {
8551                 w = ow;
8552             }
8553             this.proxy.setBounds(x, y, w, h);
8554             if(this.dynamic){
8555                 this.resizeElement();
8556             }
8557             }catch(e){}
8558         }
8559         this.fireEvent("resizing", this, x, y, w, h, e);
8560     },
8561
8562     // private
8563     handleOver : function(){
8564         if(this.enabled){
8565             this.el.addClass("x-resizable-over");
8566         }
8567     },
8568
8569     // private
8570     handleOut : function(){
8571         if(!this.resizing){
8572             this.el.removeClass("x-resizable-over");
8573         }
8574     },
8575
8576     /**
8577      * Returns the element this component is bound to.
8578      * @return {Roo.Element}
8579      */
8580     getEl : function(){
8581         return this.el;
8582     },
8583
8584     /**
8585      * Returns the resizeChild element (or null).
8586      * @return {Roo.Element}
8587      */
8588     getResizeChild : function(){
8589         return this.resizeChild;
8590     },
8591     groupHandler : function()
8592     {
8593         
8594     },
8595     /**
8596      * Destroys this resizable. If the element was wrapped and
8597      * removeEl is not true then the element remains.
8598      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8599      */
8600     destroy : function(removeEl){
8601         this.proxy.remove();
8602         if(this.overlay){
8603             this.overlay.removeAllListeners();
8604             this.overlay.remove();
8605         }
8606         var ps = Roo.Resizable.positions;
8607         for(var k in ps){
8608             if(typeof ps[k] != "function" && this[ps[k]]){
8609                 var h = this[ps[k]];
8610                 h.el.removeAllListeners();
8611                 h.el.remove();
8612             }
8613         }
8614         if(removeEl){
8615             this.el.update("");
8616             this.el.remove();
8617         }
8618     }
8619 });
8620
8621 // private
8622 // hash to map config positions to true positions
8623 Roo.Resizable.positions = {
8624     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8625     hd: "hdrag"
8626 };
8627
8628 // private
8629 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8630     if(!this.tpl){
8631         // only initialize the template if resizable is used
8632         var tpl = Roo.DomHelper.createTemplate(
8633             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8634         );
8635         tpl.compile();
8636         Roo.Resizable.Handle.prototype.tpl = tpl;
8637     }
8638     this.position = pos;
8639     this.rz = rz;
8640     // show north drag fro topdra
8641     var handlepos = pos == 'hdrag' ? 'north' : pos;
8642     
8643     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8644     if (pos == 'hdrag') {
8645         this.el.setStyle('cursor', 'pointer');
8646     }
8647     this.el.unselectable();
8648     if(transparent){
8649         this.el.setOpacity(0);
8650     }
8651     this.el.on("mousedown", this.onMouseDown, this);
8652     if(!disableTrackOver){
8653         this.el.on("mouseover", this.onMouseOver, this);
8654         this.el.on("mouseout", this.onMouseOut, this);
8655     }
8656 };
8657
8658 // private
8659 Roo.Resizable.Handle.prototype = {
8660     afterResize : function(rz){
8661         Roo.log('after?');
8662         // do nothing
8663     },
8664     // private
8665     onMouseDown : function(e){
8666         this.rz.onMouseDown(this, e);
8667     },
8668     // private
8669     onMouseOver : function(e){
8670         this.rz.handleOver(this, e);
8671     },
8672     // private
8673     onMouseOut : function(e){
8674         this.rz.handleOut(this, e);
8675     }
8676 };/*
8677  * Based on:
8678  * Ext JS Library 1.1.1
8679  * Copyright(c) 2006-2007, Ext JS, LLC.
8680  *
8681  * Originally Released Under LGPL - original licence link has changed is not relivant.
8682  *
8683  * Fork - LGPL
8684  * <script type="text/javascript">
8685  */
8686
8687 /**
8688  * @class Roo.Editor
8689  * @extends Roo.Component
8690  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8691  * @constructor
8692  * Create a new Editor
8693  * @param {Roo.form.Field} field The Field object (or descendant)
8694  * @param {Object} config The config object
8695  */
8696 Roo.Editor = function(field, config){
8697     Roo.Editor.superclass.constructor.call(this, config);
8698     this.field = field;
8699     this.addEvents({
8700         /**
8701              * @event beforestartedit
8702              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8703              * false from the handler of this event.
8704              * @param {Editor} this
8705              * @param {Roo.Element} boundEl The underlying element bound to this editor
8706              * @param {Mixed} value The field value being set
8707              */
8708         "beforestartedit" : true,
8709         /**
8710              * @event startedit
8711              * Fires when this editor is displayed
8712              * @param {Roo.Element} boundEl The underlying element bound to this editor
8713              * @param {Mixed} value The starting field value
8714              */
8715         "startedit" : true,
8716         /**
8717              * @event beforecomplete
8718              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8719              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8720              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8721              * event will not fire since no edit actually occurred.
8722              * @param {Editor} this
8723              * @param {Mixed} value The current field value
8724              * @param {Mixed} startValue The original field value
8725              */
8726         "beforecomplete" : true,
8727         /**
8728              * @event complete
8729              * Fires after editing is complete and any changed value has been written to the underlying field.
8730              * @param {Editor} this
8731              * @param {Mixed} value The current field value
8732              * @param {Mixed} startValue The original field value
8733              */
8734         "complete" : true,
8735         /**
8736          * @event specialkey
8737          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8738          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8739          * @param {Roo.form.Field} this
8740          * @param {Roo.EventObject} e The event object
8741          */
8742         "specialkey" : true
8743     });
8744 };
8745
8746 Roo.extend(Roo.Editor, Roo.Component, {
8747     /**
8748      * @cfg {Boolean/String} autosize
8749      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8750      * or "height" to adopt the height only (defaults to false)
8751      */
8752     /**
8753      * @cfg {Boolean} revertInvalid
8754      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8755      * validation fails (defaults to true)
8756      */
8757     /**
8758      * @cfg {Boolean} ignoreNoChange
8759      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8760      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8761      * will never be ignored.
8762      */
8763     /**
8764      * @cfg {Boolean} hideEl
8765      * False to keep the bound element visible while the editor is displayed (defaults to true)
8766      */
8767     /**
8768      * @cfg {Mixed} value
8769      * The data value of the underlying field (defaults to "")
8770      */
8771     value : "",
8772     /**
8773      * @cfg {String} alignment
8774      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8775      */
8776     alignment: "c-c?",
8777     /**
8778      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8779      * for bottom-right shadow (defaults to "frame")
8780      */
8781     shadow : "frame",
8782     /**
8783      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8784      */
8785     constrain : false,
8786     /**
8787      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8788      */
8789     completeOnEnter : false,
8790     /**
8791      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8792      */
8793     cancelOnEsc : false,
8794     /**
8795      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8796      */
8797     updateEl : false,
8798
8799     // private
8800     onRender : function(ct, position){
8801         this.el = new Roo.Layer({
8802             shadow: this.shadow,
8803             cls: "x-editor",
8804             parentEl : ct,
8805             shim : this.shim,
8806             shadowOffset:4,
8807             id: this.id,
8808             constrain: this.constrain
8809         });
8810         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8811         if(this.field.msgTarget != 'title'){
8812             this.field.msgTarget = 'qtip';
8813         }
8814         this.field.render(this.el);
8815         if(Roo.isGecko){
8816             this.field.el.dom.setAttribute('autocomplete', 'off');
8817         }
8818         this.field.on("specialkey", this.onSpecialKey, this);
8819         if(this.swallowKeys){
8820             this.field.el.swallowEvent(['keydown','keypress']);
8821         }
8822         this.field.show();
8823         this.field.on("blur", this.onBlur, this);
8824         if(this.field.grow){
8825             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8826         }
8827     },
8828
8829     onSpecialKey : function(field, e)
8830     {
8831         //Roo.log('editor onSpecialKey');
8832         if(this.completeOnEnter && e.getKey() == e.ENTER){
8833             e.stopEvent();
8834             this.completeEdit();
8835             return;
8836         }
8837         // do not fire special key otherwise it might hide close the editor...
8838         if(e.getKey() == e.ENTER){    
8839             return;
8840         }
8841         if(this.cancelOnEsc && e.getKey() == e.ESC){
8842             this.cancelEdit();
8843             return;
8844         } 
8845         this.fireEvent('specialkey', field, e);
8846     
8847     },
8848
8849     /**
8850      * Starts the editing process and shows the editor.
8851      * @param {String/HTMLElement/Element} el The element to edit
8852      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8853       * to the innerHTML of el.
8854      */
8855     startEdit : function(el, value){
8856         if(this.editing){
8857             this.completeEdit();
8858         }
8859         this.boundEl = Roo.get(el);
8860         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8861         if(!this.rendered){
8862             this.render(this.parentEl || document.body);
8863         }
8864         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8865             return;
8866         }
8867         this.startValue = v;
8868         this.field.setValue(v);
8869         if(this.autoSize){
8870             var sz = this.boundEl.getSize();
8871             switch(this.autoSize){
8872                 case "width":
8873                 this.setSize(sz.width,  "");
8874                 break;
8875                 case "height":
8876                 this.setSize("",  sz.height);
8877                 break;
8878                 default:
8879                 this.setSize(sz.width,  sz.height);
8880             }
8881         }
8882         this.el.alignTo(this.boundEl, this.alignment);
8883         this.editing = true;
8884         if(Roo.QuickTips){
8885             Roo.QuickTips.disable();
8886         }
8887         this.show();
8888     },
8889
8890     /**
8891      * Sets the height and width of this editor.
8892      * @param {Number} width The new width
8893      * @param {Number} height The new height
8894      */
8895     setSize : function(w, h){
8896         this.field.setSize(w, h);
8897         if(this.el){
8898             this.el.sync();
8899         }
8900     },
8901
8902     /**
8903      * Realigns the editor to the bound field based on the current alignment config value.
8904      */
8905     realign : function(){
8906         this.el.alignTo(this.boundEl, this.alignment);
8907     },
8908
8909     /**
8910      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8911      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8912      */
8913     completeEdit : function(remainVisible){
8914         if(!this.editing){
8915             return;
8916         }
8917         var v = this.getValue();
8918         if(this.revertInvalid !== false && !this.field.isValid()){
8919             v = this.startValue;
8920             this.cancelEdit(true);
8921         }
8922         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8923             this.editing = false;
8924             this.hide();
8925             return;
8926         }
8927         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8928             this.editing = false;
8929             if(this.updateEl && this.boundEl){
8930                 this.boundEl.update(v);
8931             }
8932             if(remainVisible !== true){
8933                 this.hide();
8934             }
8935             this.fireEvent("complete", this, v, this.startValue);
8936         }
8937     },
8938
8939     // private
8940     onShow : function(){
8941         this.el.show();
8942         if(this.hideEl !== false){
8943             this.boundEl.hide();
8944         }
8945         this.field.show();
8946         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8947             this.fixIEFocus = true;
8948             this.deferredFocus.defer(50, this);
8949         }else{
8950             this.field.focus();
8951         }
8952         this.fireEvent("startedit", this.boundEl, this.startValue);
8953     },
8954
8955     deferredFocus : function(){
8956         if(this.editing){
8957             this.field.focus();
8958         }
8959     },
8960
8961     /**
8962      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8963      * reverted to the original starting value.
8964      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8965      * cancel (defaults to false)
8966      */
8967     cancelEdit : function(remainVisible){
8968         if(this.editing){
8969             this.setValue(this.startValue);
8970             if(remainVisible !== true){
8971                 this.hide();
8972             }
8973         }
8974     },
8975
8976     // private
8977     onBlur : function(){
8978         if(this.allowBlur !== true && this.editing){
8979             this.completeEdit();
8980         }
8981     },
8982
8983     // private
8984     onHide : function(){
8985         if(this.editing){
8986             this.completeEdit();
8987             return;
8988         }
8989         this.field.blur();
8990         if(this.field.collapse){
8991             this.field.collapse();
8992         }
8993         this.el.hide();
8994         if(this.hideEl !== false){
8995             this.boundEl.show();
8996         }
8997         if(Roo.QuickTips){
8998             Roo.QuickTips.enable();
8999         }
9000     },
9001
9002     /**
9003      * Sets the data value of the editor
9004      * @param {Mixed} value Any valid value supported by the underlying field
9005      */
9006     setValue : function(v){
9007         this.field.setValue(v);
9008     },
9009
9010     /**
9011      * Gets the data value of the editor
9012      * @return {Mixed} The data value
9013      */
9014     getValue : function(){
9015         return this.field.getValue();
9016     }
9017 });/*
9018  * Based on:
9019  * Ext JS Library 1.1.1
9020  * Copyright(c) 2006-2007, Ext JS, LLC.
9021  *
9022  * Originally Released Under LGPL - original licence link has changed is not relivant.
9023  *
9024  * Fork - LGPL
9025  * <script type="text/javascript">
9026  */
9027  
9028 /**
9029  * @class Roo.BasicDialog
9030  * @extends Roo.util.Observable
9031  * @parent none builder
9032  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9033  * <pre><code>
9034 var dlg = new Roo.BasicDialog("my-dlg", {
9035     height: 200,
9036     width: 300,
9037     minHeight: 100,
9038     minWidth: 150,
9039     modal: true,
9040     proxyDrag: true,
9041     shadow: true
9042 });
9043 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9044 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9045 dlg.addButton('Cancel', dlg.hide, dlg);
9046 dlg.show();
9047 </code></pre>
9048   <b>A Dialog should always be a direct child of the body element.</b>
9049  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9050  * @cfg {String} title Default text to display in the title bar (defaults to null)
9051  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9052  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9053  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9054  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9055  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9056  * (defaults to null with no animation)
9057  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9058  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9059  * property for valid values (defaults to 'all')
9060  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9061  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9062  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9063  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9064  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9065  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9066  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9067  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9068  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9069  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9070  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9071  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9072  * draggable = true (defaults to false)
9073  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9074  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9075  * shadow (defaults to false)
9076  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9077  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9078  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9079  * @cfg {Array} buttons Array of buttons
9080  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9081  * @constructor
9082  * Create a new BasicDialog.
9083  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9084  * @param {Object} config Configuration options
9085  */
9086 Roo.BasicDialog = function(el, config){
9087     this.el = Roo.get(el);
9088     var dh = Roo.DomHelper;
9089     if(!this.el && config && config.autoCreate){
9090         if(typeof config.autoCreate == "object"){
9091             if(!config.autoCreate.id){
9092                 config.autoCreate.id = el;
9093             }
9094             this.el = dh.append(document.body,
9095                         config.autoCreate, true);
9096         }else{
9097             this.el = dh.append(document.body,
9098                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9099         }
9100     }
9101     el = this.el;
9102     el.setDisplayed(true);
9103     el.hide = this.hideAction;
9104     this.id = el.id;
9105     el.addClass("x-dlg");
9106
9107     Roo.apply(this, config);
9108
9109     this.proxy = el.createProxy("x-dlg-proxy");
9110     this.proxy.hide = this.hideAction;
9111     this.proxy.setOpacity(.5);
9112     this.proxy.hide();
9113
9114     if(config.width){
9115         el.setWidth(config.width);
9116     }
9117     if(config.height){
9118         el.setHeight(config.height);
9119     }
9120     this.size = el.getSize();
9121     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9122         this.xy = [config.x,config.y];
9123     }else{
9124         this.xy = el.getCenterXY(true);
9125     }
9126     /** The header element @type Roo.Element */
9127     this.header = el.child("> .x-dlg-hd");
9128     /** The body element @type Roo.Element */
9129     this.body = el.child("> .x-dlg-bd");
9130     /** The footer element @type Roo.Element */
9131     this.footer = el.child("> .x-dlg-ft");
9132
9133     if(!this.header){
9134         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9135     }
9136     if(!this.body){
9137         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9138     }
9139
9140     this.header.unselectable();
9141     if(this.title){
9142         this.header.update(this.title);
9143     }
9144     // this element allows the dialog to be focused for keyboard event
9145     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9146     this.focusEl.swallowEvent("click", true);
9147
9148     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9149
9150     // wrap the body and footer for special rendering
9151     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9152     if(this.footer){
9153         this.bwrap.dom.appendChild(this.footer.dom);
9154     }
9155
9156     this.bg = this.el.createChild({
9157         tag: "div", cls:"x-dlg-bg",
9158         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9159     });
9160     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9161
9162
9163     if(this.autoScroll !== false && !this.autoTabs){
9164         this.body.setStyle("overflow", "auto");
9165     }
9166
9167     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9168
9169     if(this.closable !== false){
9170         this.el.addClass("x-dlg-closable");
9171         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9172         this.close.on("click", this.closeClick, this);
9173         this.close.addClassOnOver("x-dlg-close-over");
9174     }
9175     if(this.collapsible !== false){
9176         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9177         this.collapseBtn.on("click", this.collapseClick, this);
9178         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9179         this.header.on("dblclick", this.collapseClick, this);
9180     }
9181     if(this.resizable !== false){
9182         this.el.addClass("x-dlg-resizable");
9183         this.resizer = new Roo.Resizable(el, {
9184             minWidth: this.minWidth || 80,
9185             minHeight:this.minHeight || 80,
9186             handles: this.resizeHandles || "all",
9187             pinned: true
9188         });
9189         this.resizer.on("beforeresize", this.beforeResize, this);
9190         this.resizer.on("resize", this.onResize, this);
9191     }
9192     if(this.draggable !== false){
9193         el.addClass("x-dlg-draggable");
9194         if (!this.proxyDrag) {
9195             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9196         }
9197         else {
9198             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9199         }
9200         dd.setHandleElId(this.header.id);
9201         dd.endDrag = this.endMove.createDelegate(this);
9202         dd.startDrag = this.startMove.createDelegate(this);
9203         dd.onDrag = this.onDrag.createDelegate(this);
9204         dd.scroll = false;
9205         this.dd = dd;
9206     }
9207     if(this.modal){
9208         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9209         this.mask.enableDisplayMode("block");
9210         this.mask.hide();
9211         this.el.addClass("x-dlg-modal");
9212     }
9213     if(this.shadow){
9214         this.shadow = new Roo.Shadow({
9215             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9216             offset : this.shadowOffset
9217         });
9218     }else{
9219         this.shadowOffset = 0;
9220     }
9221     if(Roo.useShims && this.shim !== false){
9222         this.shim = this.el.createShim();
9223         this.shim.hide = this.hideAction;
9224         this.shim.hide();
9225     }else{
9226         this.shim = false;
9227     }
9228     if(this.autoTabs){
9229         this.initTabs();
9230     }
9231     if (this.buttons) { 
9232         var bts= this.buttons;
9233         this.buttons = [];
9234         Roo.each(bts, function(b) {
9235             this.addButton(b);
9236         }, this);
9237     }
9238     
9239     
9240     this.addEvents({
9241         /**
9242          * @event keydown
9243          * Fires when a key is pressed
9244          * @param {Roo.BasicDialog} this
9245          * @param {Roo.EventObject} e
9246          */
9247         "keydown" : true,
9248         /**
9249          * @event move
9250          * Fires when this dialog is moved by the user.
9251          * @param {Roo.BasicDialog} this
9252          * @param {Number} x The new page X
9253          * @param {Number} y The new page Y
9254          */
9255         "move" : true,
9256         /**
9257          * @event resize
9258          * Fires when this dialog is resized by the user.
9259          * @param {Roo.BasicDialog} this
9260          * @param {Number} width The new width
9261          * @param {Number} height The new height
9262          */
9263         "resize" : true,
9264         /**
9265          * @event beforehide
9266          * Fires before this dialog is hidden.
9267          * @param {Roo.BasicDialog} this
9268          */
9269         "beforehide" : true,
9270         /**
9271          * @event hide
9272          * Fires when this dialog is hidden.
9273          * @param {Roo.BasicDialog} this
9274          */
9275         "hide" : true,
9276         /**
9277          * @event beforeshow
9278          * Fires before this dialog is shown.
9279          * @param {Roo.BasicDialog} this
9280          */
9281         "beforeshow" : true,
9282         /**
9283          * @event show
9284          * Fires when this dialog is shown.
9285          * @param {Roo.BasicDialog} this
9286          */
9287         "show" : true
9288     });
9289     el.on("keydown", this.onKeyDown, this);
9290     el.on("mousedown", this.toFront, this);
9291     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9292     this.el.hide();
9293     Roo.DialogManager.register(this);
9294     Roo.BasicDialog.superclass.constructor.call(this);
9295 };
9296
9297 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9298     shadowOffset: Roo.isIE ? 6 : 5,
9299     minHeight: 80,
9300     minWidth: 200,
9301     minButtonWidth: 75,
9302     defaultButton: null,
9303     buttonAlign: "right",
9304     tabTag: 'div',
9305     firstShow: true,
9306
9307     /**
9308      * Sets the dialog title text
9309      * @param {String} text The title text to display
9310      * @return {Roo.BasicDialog} this
9311      */
9312     setTitle : function(text){
9313         this.header.update(text);
9314         return this;
9315     },
9316
9317     // private
9318     closeClick : function(){
9319         this.hide();
9320     },
9321
9322     // private
9323     collapseClick : function(){
9324         this[this.collapsed ? "expand" : "collapse"]();
9325     },
9326
9327     /**
9328      * Collapses the dialog to its minimized state (only the title bar is visible).
9329      * Equivalent to the user clicking the collapse dialog button.
9330      */
9331     collapse : function(){
9332         if(!this.collapsed){
9333             this.collapsed = true;
9334             this.el.addClass("x-dlg-collapsed");
9335             this.restoreHeight = this.el.getHeight();
9336             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9337         }
9338     },
9339
9340     /**
9341      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9342      * clicking the expand dialog button.
9343      */
9344     expand : function(){
9345         if(this.collapsed){
9346             this.collapsed = false;
9347             this.el.removeClass("x-dlg-collapsed");
9348             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9349         }
9350     },
9351
9352     /**
9353      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9354      * @return {Roo.TabPanel} The tabs component
9355      */
9356     initTabs : function(){
9357         var tabs = this.getTabs();
9358         while(tabs.getTab(0)){
9359             tabs.removeTab(0);
9360         }
9361         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9362             var dom = el.dom;
9363             tabs.addTab(Roo.id(dom), dom.title);
9364             dom.title = "";
9365         });
9366         tabs.activate(0);
9367         return tabs;
9368     },
9369
9370     // private
9371     beforeResize : function(){
9372         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9373     },
9374
9375     // private
9376     onResize : function(){
9377         this.refreshSize();
9378         this.syncBodyHeight();
9379         this.adjustAssets();
9380         this.focus();
9381         this.fireEvent("resize", this, this.size.width, this.size.height);
9382     },
9383
9384     // private
9385     onKeyDown : function(e){
9386         if(this.isVisible()){
9387             this.fireEvent("keydown", this, e);
9388         }
9389     },
9390
9391     /**
9392      * Resizes the dialog.
9393      * @param {Number} width
9394      * @param {Number} height
9395      * @return {Roo.BasicDialog} this
9396      */
9397     resizeTo : function(width, height){
9398         this.el.setSize(width, height);
9399         this.size = {width: width, height: height};
9400         this.syncBodyHeight();
9401         if(this.fixedcenter){
9402             this.center();
9403         }
9404         if(this.isVisible()){
9405             this.constrainXY();
9406             this.adjustAssets();
9407         }
9408         this.fireEvent("resize", this, width, height);
9409         return this;
9410     },
9411
9412
9413     /**
9414      * Resizes the dialog to fit the specified content size.
9415      * @param {Number} width
9416      * @param {Number} height
9417      * @return {Roo.BasicDialog} this
9418      */
9419     setContentSize : function(w, h){
9420         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9421         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9422         //if(!this.el.isBorderBox()){
9423             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9424             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9425         //}
9426         if(this.tabs){
9427             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9428             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9429         }
9430         this.resizeTo(w, h);
9431         return this;
9432     },
9433
9434     /**
9435      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9436      * executed in response to a particular key being pressed while the dialog is active.
9437      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9438      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9439      * @param {Function} fn The function to call
9440      * @param {Object} scope (optional) The scope of the function
9441      * @return {Roo.BasicDialog} this
9442      */
9443     addKeyListener : function(key, fn, scope){
9444         var keyCode, shift, ctrl, alt;
9445         if(typeof key == "object" && !(key instanceof Array)){
9446             keyCode = key["key"];
9447             shift = key["shift"];
9448             ctrl = key["ctrl"];
9449             alt = key["alt"];
9450         }else{
9451             keyCode = key;
9452         }
9453         var handler = function(dlg, e){
9454             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9455                 var k = e.getKey();
9456                 if(keyCode instanceof Array){
9457                     for(var i = 0, len = keyCode.length; i < len; i++){
9458                         if(keyCode[i] == k){
9459                           fn.call(scope || window, dlg, k, e);
9460                           return;
9461                         }
9462                     }
9463                 }else{
9464                     if(k == keyCode){
9465                         fn.call(scope || window, dlg, k, e);
9466                     }
9467                 }
9468             }
9469         };
9470         this.on("keydown", handler);
9471         return this;
9472     },
9473
9474     /**
9475      * Returns the TabPanel component (creates it if it doesn't exist).
9476      * Note: If you wish to simply check for the existence of tabs without creating them,
9477      * check for a null 'tabs' property.
9478      * @return {Roo.TabPanel} The tabs component
9479      */
9480     getTabs : function(){
9481         if(!this.tabs){
9482             this.el.addClass("x-dlg-auto-tabs");
9483             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9484             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9485         }
9486         return this.tabs;
9487     },
9488
9489     /**
9490      * Adds a button to the footer section of the dialog.
9491      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9492      * object or a valid Roo.DomHelper element config
9493      * @param {Function} handler The function called when the button is clicked
9494      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9495      * @return {Roo.Button} The new button
9496      */
9497     addButton : function(config, handler, scope){
9498         var dh = Roo.DomHelper;
9499         if(!this.footer){
9500             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9501         }
9502         if(!this.btnContainer){
9503             var tb = this.footer.createChild({
9504
9505                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9506                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9507             }, null, true);
9508             this.btnContainer = tb.firstChild.firstChild.firstChild;
9509         }
9510         var bconfig = {
9511             handler: handler,
9512             scope: scope,
9513             minWidth: this.minButtonWidth,
9514             hideParent:true
9515         };
9516         if(typeof config == "string"){
9517             bconfig.text = config;
9518         }else{
9519             if(config.tag){
9520                 bconfig.dhconfig = config;
9521             }else{
9522                 Roo.apply(bconfig, config);
9523             }
9524         }
9525         var fc = false;
9526         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9527             bconfig.position = Math.max(0, bconfig.position);
9528             fc = this.btnContainer.childNodes[bconfig.position];
9529         }
9530          
9531         var btn = new Roo.Button(
9532             fc ? 
9533                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9534                 : this.btnContainer.appendChild(document.createElement("td")),
9535             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9536             bconfig
9537         );
9538         this.syncBodyHeight();
9539         if(!this.buttons){
9540             /**
9541              * Array of all the buttons that have been added to this dialog via addButton
9542              * @type Array
9543              */
9544             this.buttons = [];
9545         }
9546         this.buttons.push(btn);
9547         return btn;
9548     },
9549
9550     /**
9551      * Sets the default button to be focused when the dialog is displayed.
9552      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9553      * @return {Roo.BasicDialog} this
9554      */
9555     setDefaultButton : function(btn){
9556         this.defaultButton = btn;
9557         return this;
9558     },
9559
9560     // private
9561     getHeaderFooterHeight : function(safe){
9562         var height = 0;
9563         if(this.header){
9564            height += this.header.getHeight();
9565         }
9566         if(this.footer){
9567            var fm = this.footer.getMargins();
9568             height += (this.footer.getHeight()+fm.top+fm.bottom);
9569         }
9570         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9571         height += this.centerBg.getPadding("tb");
9572         return height;
9573     },
9574
9575     // private
9576     syncBodyHeight : function()
9577     {
9578         var bd = this.body, // the text
9579             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9580             bw = this.bwrap;
9581         var height = this.size.height - this.getHeaderFooterHeight(false);
9582         bd.setHeight(height-bd.getMargins("tb"));
9583         var hh = this.header.getHeight();
9584         var h = this.size.height-hh;
9585         cb.setHeight(h);
9586         
9587         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9588         bw.setHeight(h-cb.getPadding("tb"));
9589         
9590         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9591         bd.setWidth(bw.getWidth(true));
9592         if(this.tabs){
9593             this.tabs.syncHeight();
9594             if(Roo.isIE){
9595                 this.tabs.el.repaint();
9596             }
9597         }
9598     },
9599
9600     /**
9601      * Restores the previous state of the dialog if Roo.state is configured.
9602      * @return {Roo.BasicDialog} this
9603      */
9604     restoreState : function(){
9605         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9606         if(box && box.width){
9607             this.xy = [box.x, box.y];
9608             this.resizeTo(box.width, box.height);
9609         }
9610         return this;
9611     },
9612
9613     // private
9614     beforeShow : function(){
9615         this.expand();
9616         if(this.fixedcenter){
9617             this.xy = this.el.getCenterXY(true);
9618         }
9619         if(this.modal){
9620             Roo.get(document.body).addClass("x-body-masked");
9621             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9622             this.mask.show();
9623         }
9624         this.constrainXY();
9625     },
9626
9627     // private
9628     animShow : function(){
9629         var b = Roo.get(this.animateTarget).getBox();
9630         this.proxy.setSize(b.width, b.height);
9631         this.proxy.setLocation(b.x, b.y);
9632         this.proxy.show();
9633         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9634                     true, .35, this.showEl.createDelegate(this));
9635     },
9636
9637     /**
9638      * Shows the dialog.
9639      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9640      * @return {Roo.BasicDialog} this
9641      */
9642     show : function(animateTarget){
9643         if (this.fireEvent("beforeshow", this) === false){
9644             return;
9645         }
9646         if(this.syncHeightBeforeShow){
9647             this.syncBodyHeight();
9648         }else if(this.firstShow){
9649             this.firstShow = false;
9650             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9651         }
9652         this.animateTarget = animateTarget || this.animateTarget;
9653         if(!this.el.isVisible()){
9654             this.beforeShow();
9655             if(this.animateTarget && Roo.get(this.animateTarget)){
9656                 this.animShow();
9657             }else{
9658                 this.showEl();
9659             }
9660         }
9661         return this;
9662     },
9663
9664     // private
9665     showEl : function(){
9666         this.proxy.hide();
9667         this.el.setXY(this.xy);
9668         this.el.show();
9669         this.adjustAssets(true);
9670         this.toFront();
9671         this.focus();
9672         // IE peekaboo bug - fix found by Dave Fenwick
9673         if(Roo.isIE){
9674             this.el.repaint();
9675         }
9676         this.fireEvent("show", this);
9677     },
9678
9679     /**
9680      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9681      * dialog itself will receive focus.
9682      */
9683     focus : function(){
9684         if(this.defaultButton){
9685             this.defaultButton.focus();
9686         }else{
9687             this.focusEl.focus();
9688         }
9689     },
9690
9691     // private
9692     constrainXY : function(){
9693         if(this.constraintoviewport !== false){
9694             if(!this.viewSize){
9695                 if(this.container){
9696                     var s = this.container.getSize();
9697                     this.viewSize = [s.width, s.height];
9698                 }else{
9699                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9700                 }
9701             }
9702             var s = Roo.get(this.container||document).getScroll();
9703
9704             var x = this.xy[0], y = this.xy[1];
9705             var w = this.size.width, h = this.size.height;
9706             var vw = this.viewSize[0], vh = this.viewSize[1];
9707             // only move it if it needs it
9708             var moved = false;
9709             // first validate right/bottom
9710             if(x + w > vw+s.left){
9711                 x = vw - w;
9712                 moved = true;
9713             }
9714             if(y + h > vh+s.top){
9715                 y = vh - h;
9716                 moved = true;
9717             }
9718             // then make sure top/left isn't negative
9719             if(x < s.left){
9720                 x = s.left;
9721                 moved = true;
9722             }
9723             if(y < s.top){
9724                 y = s.top;
9725                 moved = true;
9726             }
9727             if(moved){
9728                 // cache xy
9729                 this.xy = [x, y];
9730                 if(this.isVisible()){
9731                     this.el.setLocation(x, y);
9732                     this.adjustAssets();
9733                 }
9734             }
9735         }
9736     },
9737
9738     // private
9739     onDrag : function(){
9740         if(!this.proxyDrag){
9741             this.xy = this.el.getXY();
9742             this.adjustAssets();
9743         }
9744     },
9745
9746     // private
9747     adjustAssets : function(doShow){
9748         var x = this.xy[0], y = this.xy[1];
9749         var w = this.size.width, h = this.size.height;
9750         if(doShow === true){
9751             if(this.shadow){
9752                 this.shadow.show(this.el);
9753             }
9754             if(this.shim){
9755                 this.shim.show();
9756             }
9757         }
9758         if(this.shadow && this.shadow.isVisible()){
9759             this.shadow.show(this.el);
9760         }
9761         if(this.shim && this.shim.isVisible()){
9762             this.shim.setBounds(x, y, w, h);
9763         }
9764     },
9765
9766     // private
9767     adjustViewport : function(w, h){
9768         if(!w || !h){
9769             w = Roo.lib.Dom.getViewWidth();
9770             h = Roo.lib.Dom.getViewHeight();
9771         }
9772         // cache the size
9773         this.viewSize = [w, h];
9774         if(this.modal && this.mask.isVisible()){
9775             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9776             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9777         }
9778         if(this.isVisible()){
9779             this.constrainXY();
9780         }
9781     },
9782
9783     /**
9784      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9785      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9786      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9787      */
9788     destroy : function(removeEl){
9789         if(this.isVisible()){
9790             this.animateTarget = null;
9791             this.hide();
9792         }
9793         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9794         if(this.tabs){
9795             this.tabs.destroy(removeEl);
9796         }
9797         Roo.destroy(
9798              this.shim,
9799              this.proxy,
9800              this.resizer,
9801              this.close,
9802              this.mask
9803         );
9804         if(this.dd){
9805             this.dd.unreg();
9806         }
9807         if(this.buttons){
9808            for(var i = 0, len = this.buttons.length; i < len; i++){
9809                this.buttons[i].destroy();
9810            }
9811         }
9812         this.el.removeAllListeners();
9813         if(removeEl === true){
9814             this.el.update("");
9815             this.el.remove();
9816         }
9817         Roo.DialogManager.unregister(this);
9818     },
9819
9820     // private
9821     startMove : function(){
9822         if(this.proxyDrag){
9823             this.proxy.show();
9824         }
9825         if(this.constraintoviewport !== false){
9826             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9827         }
9828     },
9829
9830     // private
9831     endMove : function(){
9832         if(!this.proxyDrag){
9833             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9834         }else{
9835             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9836             this.proxy.hide();
9837         }
9838         this.refreshSize();
9839         this.adjustAssets();
9840         this.focus();
9841         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9842     },
9843
9844     /**
9845      * Brings this dialog to the front of any other visible dialogs
9846      * @return {Roo.BasicDialog} this
9847      */
9848     toFront : function(){
9849         Roo.DialogManager.bringToFront(this);
9850         return this;
9851     },
9852
9853     /**
9854      * Sends this dialog to the back (under) of any other visible dialogs
9855      * @return {Roo.BasicDialog} this
9856      */
9857     toBack : function(){
9858         Roo.DialogManager.sendToBack(this);
9859         return this;
9860     },
9861
9862     /**
9863      * Centers this dialog in the viewport
9864      * @return {Roo.BasicDialog} this
9865      */
9866     center : function(){
9867         var xy = this.el.getCenterXY(true);
9868         this.moveTo(xy[0], xy[1]);
9869         return this;
9870     },
9871
9872     /**
9873      * Moves the dialog's top-left corner to the specified point
9874      * @param {Number} x
9875      * @param {Number} y
9876      * @return {Roo.BasicDialog} this
9877      */
9878     moveTo : function(x, y){
9879         this.xy = [x,y];
9880         if(this.isVisible()){
9881             this.el.setXY(this.xy);
9882             this.adjustAssets();
9883         }
9884         return this;
9885     },
9886
9887     /**
9888      * Aligns the dialog to the specified element
9889      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9890      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9891      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9892      * @return {Roo.BasicDialog} this
9893      */
9894     alignTo : function(element, position, offsets){
9895         this.xy = this.el.getAlignToXY(element, position, offsets);
9896         if(this.isVisible()){
9897             this.el.setXY(this.xy);
9898             this.adjustAssets();
9899         }
9900         return this;
9901     },
9902
9903     /**
9904      * Anchors an element to another element and realigns it when the window is resized.
9905      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9906      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9907      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9908      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9909      * is a number, it is used as the buffer delay (defaults to 50ms).
9910      * @return {Roo.BasicDialog} this
9911      */
9912     anchorTo : function(el, alignment, offsets, monitorScroll){
9913         var action = function(){
9914             this.alignTo(el, alignment, offsets);
9915         };
9916         Roo.EventManager.onWindowResize(action, this);
9917         var tm = typeof monitorScroll;
9918         if(tm != 'undefined'){
9919             Roo.EventManager.on(window, 'scroll', action, this,
9920                 {buffer: tm == 'number' ? monitorScroll : 50});
9921         }
9922         action.call(this);
9923         return this;
9924     },
9925
9926     /**
9927      * Returns true if the dialog is visible
9928      * @return {Boolean}
9929      */
9930     isVisible : function(){
9931         return this.el.isVisible();
9932     },
9933
9934     // private
9935     animHide : function(callback){
9936         var b = Roo.get(this.animateTarget).getBox();
9937         this.proxy.show();
9938         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9939         this.el.hide();
9940         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9941                     this.hideEl.createDelegate(this, [callback]));
9942     },
9943
9944     /**
9945      * Hides the dialog.
9946      * @param {Function} callback (optional) Function to call when the dialog is hidden
9947      * @return {Roo.BasicDialog} this
9948      */
9949     hide : function(callback){
9950         if (this.fireEvent("beforehide", this) === false){
9951             return;
9952         }
9953         if(this.shadow){
9954             this.shadow.hide();
9955         }
9956         if(this.shim) {
9957           this.shim.hide();
9958         }
9959         // sometimes animateTarget seems to get set.. causing problems...
9960         // this just double checks..
9961         if(this.animateTarget && Roo.get(this.animateTarget)) {
9962            this.animHide(callback);
9963         }else{
9964             this.el.hide();
9965             this.hideEl(callback);
9966         }
9967         return this;
9968     },
9969
9970     // private
9971     hideEl : function(callback){
9972         this.proxy.hide();
9973         if(this.modal){
9974             this.mask.hide();
9975             Roo.get(document.body).removeClass("x-body-masked");
9976         }
9977         this.fireEvent("hide", this);
9978         if(typeof callback == "function"){
9979             callback();
9980         }
9981     },
9982
9983     // private
9984     hideAction : function(){
9985         this.setLeft("-10000px");
9986         this.setTop("-10000px");
9987         this.setStyle("visibility", "hidden");
9988     },
9989
9990     // private
9991     refreshSize : function(){
9992         this.size = this.el.getSize();
9993         this.xy = this.el.getXY();
9994         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9995     },
9996
9997     // private
9998     // z-index is managed by the DialogManager and may be overwritten at any time
9999     setZIndex : function(index){
10000         if(this.modal){
10001             this.mask.setStyle("z-index", index);
10002         }
10003         if(this.shim){
10004             this.shim.setStyle("z-index", ++index);
10005         }
10006         if(this.shadow){
10007             this.shadow.setZIndex(++index);
10008         }
10009         this.el.setStyle("z-index", ++index);
10010         if(this.proxy){
10011             this.proxy.setStyle("z-index", ++index);
10012         }
10013         if(this.resizer){
10014             this.resizer.proxy.setStyle("z-index", ++index);
10015         }
10016
10017         this.lastZIndex = index;
10018     },
10019
10020     /**
10021      * Returns the element for this dialog
10022      * @return {Roo.Element} The underlying dialog Element
10023      */
10024     getEl : function(){
10025         return this.el;
10026     }
10027 });
10028
10029 /**
10030  * @class Roo.DialogManager
10031  * Provides global access to BasicDialogs that have been created and
10032  * support for z-indexing (layering) multiple open dialogs.
10033  */
10034 Roo.DialogManager = function(){
10035     var list = {};
10036     var accessList = [];
10037     var front = null;
10038
10039     // private
10040     var sortDialogs = function(d1, d2){
10041         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10042     };
10043
10044     // private
10045     var orderDialogs = function(){
10046         accessList.sort(sortDialogs);
10047         var seed = Roo.DialogManager.zseed;
10048         for(var i = 0, len = accessList.length; i < len; i++){
10049             var dlg = accessList[i];
10050             if(dlg){
10051                 dlg.setZIndex(seed + (i*10));
10052             }
10053         }
10054     };
10055
10056     return {
10057         /**
10058          * The starting z-index for BasicDialogs (defaults to 9000)
10059          * @type Number The z-index value
10060          */
10061         zseed : 9000,
10062
10063         // private
10064         register : function(dlg){
10065             list[dlg.id] = dlg;
10066             accessList.push(dlg);
10067         },
10068
10069         // private
10070         unregister : function(dlg){
10071             delete list[dlg.id];
10072             var i=0;
10073             var len=0;
10074             if(!accessList.indexOf){
10075                 for(  i = 0, len = accessList.length; i < len; i++){
10076                     if(accessList[i] == dlg){
10077                         accessList.splice(i, 1);
10078                         return;
10079                     }
10080                 }
10081             }else{
10082                  i = accessList.indexOf(dlg);
10083                 if(i != -1){
10084                     accessList.splice(i, 1);
10085                 }
10086             }
10087         },
10088
10089         /**
10090          * Gets a registered dialog by id
10091          * @param {String/Object} id The id of the dialog or a dialog
10092          * @return {Roo.BasicDialog} this
10093          */
10094         get : function(id){
10095             return typeof id == "object" ? id : list[id];
10096         },
10097
10098         /**
10099          * Brings the specified dialog to the front
10100          * @param {String/Object} dlg The id of the dialog or a dialog
10101          * @return {Roo.BasicDialog} this
10102          */
10103         bringToFront : function(dlg){
10104             dlg = this.get(dlg);
10105             if(dlg != front){
10106                 front = dlg;
10107                 dlg._lastAccess = new Date().getTime();
10108                 orderDialogs();
10109             }
10110             return dlg;
10111         },
10112
10113         /**
10114          * Sends the specified dialog to the back
10115          * @param {String/Object} dlg The id of the dialog or a dialog
10116          * @return {Roo.BasicDialog} this
10117          */
10118         sendToBack : function(dlg){
10119             dlg = this.get(dlg);
10120             dlg._lastAccess = -(new Date().getTime());
10121             orderDialogs();
10122             return dlg;
10123         },
10124
10125         /**
10126          * Hides all dialogs
10127          */
10128         hideAll : function(){
10129             for(var id in list){
10130                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10131                     list[id].hide();
10132                 }
10133             }
10134         }
10135     };
10136 }();
10137
10138 /**
10139  * @class Roo.LayoutDialog
10140  * @extends Roo.BasicDialog
10141  * @children Roo.ContentPanel
10142  * @parent builder none
10143  * Dialog which provides adjustments for working with a layout in a Dialog.
10144  * Add your necessary layout config options to the dialog's config.<br>
10145  * Example usage (including a nested layout):
10146  * <pre><code>
10147 if(!dialog){
10148     dialog = new Roo.LayoutDialog("download-dlg", {
10149         modal: true,
10150         width:600,
10151         height:450,
10152         shadow:true,
10153         minWidth:500,
10154         minHeight:350,
10155         autoTabs:true,
10156         proxyDrag:true,
10157         // layout config merges with the dialog config
10158         center:{
10159             tabPosition: "top",
10160             alwaysShowTabs: true
10161         }
10162     });
10163     dialog.addKeyListener(27, dialog.hide, dialog);
10164     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10165     dialog.addButton("Build It!", this.getDownload, this);
10166
10167     // we can even add nested layouts
10168     var innerLayout = new Roo.BorderLayout("dl-inner", {
10169         east: {
10170             initialSize: 200,
10171             autoScroll:true,
10172             split:true
10173         },
10174         center: {
10175             autoScroll:true
10176         }
10177     });
10178     innerLayout.beginUpdate();
10179     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10180     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10181     innerLayout.endUpdate(true);
10182
10183     var layout = dialog.getLayout();
10184     layout.beginUpdate();
10185     layout.add("center", new Roo.ContentPanel("standard-panel",
10186                         {title: "Download the Source", fitToFrame:true}));
10187     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10188                {title: "Build your own roo.js"}));
10189     layout.getRegion("center").showPanel(sp);
10190     layout.endUpdate();
10191 }
10192 </code></pre>
10193     * @constructor
10194     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10195     * @param {Object} config configuration options
10196   */
10197 Roo.LayoutDialog = function(el, cfg){
10198     
10199     var config=  cfg;
10200     if (typeof(cfg) == 'undefined') {
10201         config = Roo.apply({}, el);
10202         // not sure why we use documentElement here.. - it should always be body.
10203         // IE7 borks horribly if we use documentElement.
10204         // webkit also does not like documentElement - it creates a body element...
10205         el = Roo.get( document.body || document.documentElement ).createChild();
10206         //config.autoCreate = true;
10207     }
10208     
10209     
10210     config.autoTabs = false;
10211     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10212     this.body.setStyle({overflow:"hidden", position:"relative"});
10213     this.layout = new Roo.BorderLayout(this.body.dom, config);
10214     this.layout.monitorWindowResize = false;
10215     this.el.addClass("x-dlg-auto-layout");
10216     // fix case when center region overwrites center function
10217     this.center = Roo.BasicDialog.prototype.center;
10218     this.on("show", this.layout.layout, this.layout, true);
10219     if (config.items) {
10220         var xitems = config.items;
10221         delete config.items;
10222         Roo.each(xitems, this.addxtype, this);
10223     }
10224     
10225     
10226 };
10227 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10228     
10229     
10230     /**
10231      * @cfg {Roo.LayoutRegion} east  
10232      */
10233     /**
10234      * @cfg {Roo.LayoutRegion} west
10235      */
10236     /**
10237      * @cfg {Roo.LayoutRegion} south
10238      */
10239     /**
10240      * @cfg {Roo.LayoutRegion} north
10241      */
10242     /**
10243      * @cfg {Roo.LayoutRegion} center
10244      */
10245     /**
10246      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10247      */
10248     
10249     
10250     /**
10251      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10252      * @deprecated
10253      */
10254     endUpdate : function(){
10255         this.layout.endUpdate();
10256     },
10257
10258     /**
10259      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10260      *  @deprecated
10261      */
10262     beginUpdate : function(){
10263         this.layout.beginUpdate();
10264     },
10265
10266     /**
10267      * Get the BorderLayout for this dialog
10268      * @return {Roo.BorderLayout}
10269      */
10270     getLayout : function(){
10271         return this.layout;
10272     },
10273
10274     showEl : function(){
10275         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10276         if(Roo.isIE7){
10277             this.layout.layout();
10278         }
10279     },
10280
10281     // private
10282     // Use the syncHeightBeforeShow config option to control this automatically
10283     syncBodyHeight : function(){
10284         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10285         if(this.layout){this.layout.layout();}
10286     },
10287     
10288       /**
10289      * Add an xtype element (actually adds to the layout.)
10290      * @return {Object} xdata xtype object data.
10291      */
10292     
10293     addxtype : function(c) {
10294         return this.layout.addxtype(c);
10295     }
10296 });/*
10297  * Based on:
10298  * Ext JS Library 1.1.1
10299  * Copyright(c) 2006-2007, Ext JS, LLC.
10300  *
10301  * Originally Released Under LGPL - original licence link has changed is not relivant.
10302  *
10303  * Fork - LGPL
10304  * <script type="text/javascript">
10305  */
10306  
10307 /**
10308  * @class Roo.MessageBox
10309  * @static
10310  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10311  * Example usage:
10312  *<pre><code>
10313 // Basic alert:
10314 Roo.Msg.alert('Status', 'Changes saved successfully.');
10315
10316 // Prompt for user data:
10317 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10318     if (btn == 'ok'){
10319         // process text value...
10320     }
10321 });
10322
10323 // Show a dialog using config options:
10324 Roo.Msg.show({
10325    title:'Save Changes?',
10326    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10327    buttons: Roo.Msg.YESNOCANCEL,
10328    fn: processResult,
10329    animEl: 'elId'
10330 });
10331 </code></pre>
10332  * @static
10333  */
10334 Roo.MessageBox = function(){
10335     var dlg, opt, mask, waitTimer;
10336     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10337     var buttons, activeTextEl, bwidth;
10338
10339     // private
10340     var handleButton = function(button){
10341         dlg.hide();
10342         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10343     };
10344
10345     // private
10346     var handleHide = function(){
10347         if(opt && opt.cls){
10348             dlg.el.removeClass(opt.cls);
10349         }
10350         if(waitTimer){
10351             Roo.TaskMgr.stop(waitTimer);
10352             waitTimer = null;
10353         }
10354     };
10355
10356     // private
10357     var updateButtons = function(b){
10358         var width = 0;
10359         if(!b){
10360             buttons["ok"].hide();
10361             buttons["cancel"].hide();
10362             buttons["yes"].hide();
10363             buttons["no"].hide();
10364             dlg.footer.dom.style.display = 'none';
10365             return width;
10366         }
10367         dlg.footer.dom.style.display = '';
10368         for(var k in buttons){
10369             if(typeof buttons[k] != "function"){
10370                 if(b[k]){
10371                     buttons[k].show();
10372                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10373                     width += buttons[k].el.getWidth()+15;
10374                 }else{
10375                     buttons[k].hide();
10376                 }
10377             }
10378         }
10379         return width;
10380     };
10381
10382     // private
10383     var handleEsc = function(d, k, e){
10384         if(opt && opt.closable !== false){
10385             dlg.hide();
10386         }
10387         if(e){
10388             e.stopEvent();
10389         }
10390     };
10391
10392     return {
10393         /**
10394          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10395          * @return {Roo.BasicDialog} The BasicDialog element
10396          */
10397         getDialog : function(){
10398            if(!dlg){
10399                 dlg = new Roo.BasicDialog("x-msg-box", {
10400                     autoCreate : true,
10401                     shadow: true,
10402                     draggable: true,
10403                     resizable:false,
10404                     constraintoviewport:false,
10405                     fixedcenter:true,
10406                     collapsible : false,
10407                     shim:true,
10408                     modal: true,
10409                     width:400, height:100,
10410                     buttonAlign:"center",
10411                     closeClick : function(){
10412                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10413                             handleButton("no");
10414                         }else{
10415                             handleButton("cancel");
10416                         }
10417                     }
10418                 });
10419               
10420                 dlg.on("hide", handleHide);
10421                 mask = dlg.mask;
10422                 dlg.addKeyListener(27, handleEsc);
10423                 buttons = {};
10424                 var bt = this.buttonText;
10425                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10426                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10427                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10428                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10429                 bodyEl = dlg.body.createChild({
10430
10431                     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>'
10432                 });
10433                 msgEl = bodyEl.dom.firstChild;
10434                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10435                 textboxEl.enableDisplayMode();
10436                 textboxEl.addKeyListener([10,13], function(){
10437                     if(dlg.isVisible() && opt && opt.buttons){
10438                         if(opt.buttons.ok){
10439                             handleButton("ok");
10440                         }else if(opt.buttons.yes){
10441                             handleButton("yes");
10442                         }
10443                     }
10444                 });
10445                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10446                 textareaEl.enableDisplayMode();
10447                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10448                 progressEl.enableDisplayMode();
10449                 var pf = progressEl.dom.firstChild;
10450                 if (pf) {
10451                     pp = Roo.get(pf.firstChild);
10452                     pp.setHeight(pf.offsetHeight);
10453                 }
10454                 
10455             }
10456             return dlg;
10457         },
10458
10459         /**
10460          * Updates the message box body text
10461          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10462          * the XHTML-compliant non-breaking space character '&amp;#160;')
10463          * @return {Roo.MessageBox} This message box
10464          */
10465         updateText : function(text){
10466             if(!dlg.isVisible() && !opt.width){
10467                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10468             }
10469             msgEl.innerHTML = text || '&#160;';
10470       
10471             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10472             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10473             var w = Math.max(
10474                     Math.min(opt.width || cw , this.maxWidth), 
10475                     Math.max(opt.minWidth || this.minWidth, bwidth)
10476             );
10477             if(opt.prompt){
10478                 activeTextEl.setWidth(w);
10479             }
10480             if(dlg.isVisible()){
10481                 dlg.fixedcenter = false;
10482             }
10483             // to big, make it scroll. = But as usual stupid IE does not support
10484             // !important..
10485             
10486             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10487                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10488                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10489             } else {
10490                 bodyEl.dom.style.height = '';
10491                 bodyEl.dom.style.overflowY = '';
10492             }
10493             if (cw > w) {
10494                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10495             } else {
10496                 bodyEl.dom.style.overflowX = '';
10497             }
10498             
10499             dlg.setContentSize(w, bodyEl.getHeight());
10500             if(dlg.isVisible()){
10501                 dlg.fixedcenter = true;
10502             }
10503             return this;
10504         },
10505
10506         /**
10507          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10508          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10509          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10510          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10511          * @return {Roo.MessageBox} This message box
10512          */
10513         updateProgress : function(value, text){
10514             if(text){
10515                 this.updateText(text);
10516             }
10517             if (pp) { // weird bug on my firefox - for some reason this is not defined
10518                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10519             }
10520             return this;
10521         },        
10522
10523         /**
10524          * Returns true if the message box is currently displayed
10525          * @return {Boolean} True if the message box is visible, else false
10526          */
10527         isVisible : function(){
10528             return dlg && dlg.isVisible();  
10529         },
10530
10531         /**
10532          * Hides the message box if it is displayed
10533          */
10534         hide : function(){
10535             if(this.isVisible()){
10536                 dlg.hide();
10537             }  
10538         },
10539
10540         /**
10541          * Displays a new message box, or reinitializes an existing message box, based on the config options
10542          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10543          * The following config object properties are supported:
10544          * <pre>
10545 Property    Type             Description
10546 ----------  ---------------  ------------------------------------------------------------------------------------
10547 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10548                                    closes (defaults to undefined)
10549 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10550                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10551 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10552                                    progress and wait dialogs will ignore this property and always hide the
10553                                    close button as they can only be closed programmatically.
10554 cls               String           A custom CSS class to apply to the message box element
10555 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10556                                    displayed (defaults to 75)
10557 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10558                                    function will be btn (the name of the button that was clicked, if applicable,
10559                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10560                                    Progress and wait dialogs will ignore this option since they do not respond to
10561                                    user actions and can only be closed programmatically, so any required function
10562                                    should be called by the same code after it closes the dialog.
10563 icon              String           A CSS class that provides a background image to be used as an icon for
10564                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10565 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10566 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10567 modal             Boolean          False to allow user interaction with the page while the message box is
10568                                    displayed (defaults to true)
10569 msg               String           A string that will replace the existing message box body text (defaults
10570                                    to the XHTML-compliant non-breaking space character '&#160;')
10571 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10572 progress          Boolean          True to display a progress bar (defaults to false)
10573 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10574 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10575 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10576 title             String           The title text
10577 value             String           The string value to set into the active textbox element if displayed
10578 wait              Boolean          True to display a progress bar (defaults to false)
10579 width             Number           The width of the dialog in pixels
10580 </pre>
10581          *
10582          * Example usage:
10583          * <pre><code>
10584 Roo.Msg.show({
10585    title: 'Address',
10586    msg: 'Please enter your address:',
10587    width: 300,
10588    buttons: Roo.MessageBox.OKCANCEL,
10589    multiline: true,
10590    fn: saveAddress,
10591    animEl: 'addAddressBtn'
10592 });
10593 </code></pre>
10594          * @param {Object} config Configuration options
10595          * @return {Roo.MessageBox} This message box
10596          */
10597         show : function(options)
10598         {
10599             
10600             // this causes nightmares if you show one dialog after another
10601             // especially on callbacks..
10602              
10603             if(this.isVisible()){
10604                 
10605                 this.hide();
10606                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10607                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10608                 Roo.log("New Dialog Message:" +  options.msg )
10609                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10610                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10611                 
10612             }
10613             var d = this.getDialog();
10614             opt = options;
10615             d.setTitle(opt.title || "&#160;");
10616             d.close.setDisplayed(opt.closable !== false);
10617             activeTextEl = textboxEl;
10618             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10619             if(opt.prompt){
10620                 if(opt.multiline){
10621                     textboxEl.hide();
10622                     textareaEl.show();
10623                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10624                         opt.multiline : this.defaultTextHeight);
10625                     activeTextEl = textareaEl;
10626                 }else{
10627                     textboxEl.show();
10628                     textareaEl.hide();
10629                 }
10630             }else{
10631                 textboxEl.hide();
10632                 textareaEl.hide();
10633             }
10634             progressEl.setDisplayed(opt.progress === true);
10635             this.updateProgress(0);
10636             activeTextEl.dom.value = opt.value || "";
10637             if(opt.prompt){
10638                 dlg.setDefaultButton(activeTextEl);
10639             }else{
10640                 var bs = opt.buttons;
10641                 var db = null;
10642                 if(bs && bs.ok){
10643                     db = buttons["ok"];
10644                 }else if(bs && bs.yes){
10645                     db = buttons["yes"];
10646                 }
10647                 dlg.setDefaultButton(db);
10648             }
10649             bwidth = updateButtons(opt.buttons);
10650             this.updateText(opt.msg);
10651             if(opt.cls){
10652                 d.el.addClass(opt.cls);
10653             }
10654             d.proxyDrag = opt.proxyDrag === true;
10655             d.modal = opt.modal !== false;
10656             d.mask = opt.modal !== false ? mask : false;
10657             if(!d.isVisible()){
10658                 // force it to the end of the z-index stack so it gets a cursor in FF
10659                 document.body.appendChild(dlg.el.dom);
10660                 d.animateTarget = null;
10661                 d.show(options.animEl);
10662             }
10663             dlg.toFront();
10664             return this;
10665         },
10666
10667         /**
10668          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10669          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10670          * and closing the message box when the process is complete.
10671          * @param {String} title The title bar text
10672          * @param {String} msg The message box body text
10673          * @return {Roo.MessageBox} This message box
10674          */
10675         progress : function(title, msg){
10676             this.show({
10677                 title : title,
10678                 msg : msg,
10679                 buttons: false,
10680                 progress:true,
10681                 closable:false,
10682                 minWidth: this.minProgressWidth,
10683                 modal : true
10684             });
10685             return this;
10686         },
10687
10688         /**
10689          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10690          * If a callback function is passed it will be called after the user clicks the button, and the
10691          * id of the button that was clicked will be passed as the only parameter to the callback
10692          * (could also be the top-right close button).
10693          * @param {String} title The title bar text
10694          * @param {String} msg The message box body text
10695          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10696          * @param {Object} scope (optional) The scope of the callback function
10697          * @return {Roo.MessageBox} This message box
10698          */
10699         alert : function(title, msg, fn, scope){
10700             this.show({
10701                 title : title,
10702                 msg : msg,
10703                 buttons: this.OK,
10704                 fn: fn,
10705                 scope : scope,
10706                 modal : true
10707             });
10708             return this;
10709         },
10710
10711         /**
10712          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10713          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10714          * You are responsible for closing the message box when the process is complete.
10715          * @param {String} msg The message box body text
10716          * @param {String} title (optional) The title bar text
10717          * @return {Roo.MessageBox} This message box
10718          */
10719         wait : function(msg, title){
10720             this.show({
10721                 title : title,
10722                 msg : msg,
10723                 buttons: false,
10724                 closable:false,
10725                 progress:true,
10726                 modal:true,
10727                 width:300,
10728                 wait:true
10729             });
10730             waitTimer = Roo.TaskMgr.start({
10731                 run: function(i){
10732                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10733                 },
10734                 interval: 1000
10735             });
10736             return this;
10737         },
10738
10739         /**
10740          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10741          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10742          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10743          * @param {String} title The title bar text
10744          * @param {String} msg The message box body text
10745          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10746          * @param {Object} scope (optional) The scope of the callback function
10747          * @return {Roo.MessageBox} This message box
10748          */
10749         confirm : function(title, msg, fn, scope){
10750             this.show({
10751                 title : title,
10752                 msg : msg,
10753                 buttons: this.YESNO,
10754                 fn: fn,
10755                 scope : scope,
10756                 modal : true
10757             });
10758             return this;
10759         },
10760
10761         /**
10762          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10763          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10764          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10765          * (could also be the top-right close button) and the text that was entered will be passed as the two
10766          * parameters to the callback.
10767          * @param {String} title The title bar text
10768          * @param {String} msg The message box body text
10769          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10770          * @param {Object} scope (optional) The scope of the callback function
10771          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10772          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10773          * @return {Roo.MessageBox} This message box
10774          */
10775         prompt : function(title, msg, fn, scope, multiline){
10776             this.show({
10777                 title : title,
10778                 msg : msg,
10779                 buttons: this.OKCANCEL,
10780                 fn: fn,
10781                 minWidth:250,
10782                 scope : scope,
10783                 prompt:true,
10784                 multiline: multiline,
10785                 modal : true
10786             });
10787             return this;
10788         },
10789
10790         /**
10791          * Button config that displays a single OK button
10792          * @type Object
10793          */
10794         OK : {ok:true},
10795         /**
10796          * Button config that displays Yes and No buttons
10797          * @type Object
10798          */
10799         YESNO : {yes:true, no:true},
10800         /**
10801          * Button config that displays OK and Cancel buttons
10802          * @type Object
10803          */
10804         OKCANCEL : {ok:true, cancel:true},
10805         /**
10806          * Button config that displays Yes, No and Cancel buttons
10807          * @type Object
10808          */
10809         YESNOCANCEL : {yes:true, no:true, cancel:true},
10810
10811         /**
10812          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10813          * @type Number
10814          */
10815         defaultTextHeight : 75,
10816         /**
10817          * The maximum width in pixels of the message box (defaults to 600)
10818          * @type Number
10819          */
10820         maxWidth : 600,
10821         /**
10822          * The minimum width in pixels of the message box (defaults to 100)
10823          * @type Number
10824          */
10825         minWidth : 100,
10826         /**
10827          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10828          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10829          * @type Number
10830          */
10831         minProgressWidth : 250,
10832         /**
10833          * An object containing the default button text strings that can be overriden for localized language support.
10834          * Supported properties are: ok, cancel, yes and no.
10835          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10836          * @type Object
10837          */
10838         buttonText : {
10839             ok : "OK",
10840             cancel : "Cancel",
10841             yes : "Yes",
10842             no : "No"
10843         }
10844     };
10845 }();
10846
10847 /**
10848  * Shorthand for {@link Roo.MessageBox}
10849  */
10850 Roo.Msg = Roo.MessageBox;/*
10851  * Based on:
10852  * Ext JS Library 1.1.1
10853  * Copyright(c) 2006-2007, Ext JS, LLC.
10854  *
10855  * Originally Released Under LGPL - original licence link has changed is not relivant.
10856  *
10857  * Fork - LGPL
10858  * <script type="text/javascript">
10859  */
10860 /**
10861  * @class Roo.QuickTips
10862  * Provides attractive and customizable tooltips for any element.
10863  * @static
10864  */
10865 Roo.QuickTips = function(){
10866     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10867     var ce, bd, xy, dd;
10868     var visible = false, disabled = true, inited = false;
10869     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10870     
10871     var onOver = function(e){
10872         if(disabled){
10873             return;
10874         }
10875         var t = e.getTarget();
10876         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10877             return;
10878         }
10879         if(ce && t == ce.el){
10880             clearTimeout(hideProc);
10881             return;
10882         }
10883         if(t && tagEls[t.id]){
10884             tagEls[t.id].el = t;
10885             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10886             return;
10887         }
10888         var ttp, et = Roo.fly(t);
10889         var ns = cfg.namespace;
10890         if(tm.interceptTitles && t.title){
10891             ttp = t.title;
10892             t.qtip = ttp;
10893             t.removeAttribute("title");
10894             e.preventDefault();
10895         }else{
10896             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10897         }
10898         if(ttp){
10899             showProc = show.defer(tm.showDelay, tm, [{
10900                 el: t, 
10901                 text: ttp.replace(/\\n/g,'<br/>'),
10902                 width: et.getAttributeNS(ns, cfg.width),
10903                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10904                 title: et.getAttributeNS(ns, cfg.title),
10905                     cls: et.getAttributeNS(ns, cfg.cls)
10906             }]);
10907         }
10908     };
10909     
10910     var onOut = function(e){
10911         clearTimeout(showProc);
10912         var t = e.getTarget();
10913         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10914             hideProc = setTimeout(hide, tm.hideDelay);
10915         }
10916     };
10917     
10918     var onMove = function(e){
10919         if(disabled){
10920             return;
10921         }
10922         xy = e.getXY();
10923         xy[1] += 18;
10924         if(tm.trackMouse && ce){
10925             el.setXY(xy);
10926         }
10927     };
10928     
10929     var onDown = function(e){
10930         clearTimeout(showProc);
10931         clearTimeout(hideProc);
10932         if(!e.within(el)){
10933             if(tm.hideOnClick){
10934                 hide();
10935                 tm.disable();
10936                 tm.enable.defer(100, tm);
10937             }
10938         }
10939     };
10940     
10941     var getPad = function(){
10942         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10943     };
10944
10945     var show = function(o){
10946         if(disabled){
10947             return;
10948         }
10949         clearTimeout(dismissProc);
10950         ce = o;
10951         if(removeCls){ // in case manually hidden
10952             el.removeClass(removeCls);
10953             removeCls = null;
10954         }
10955         if(ce.cls){
10956             el.addClass(ce.cls);
10957             removeCls = ce.cls;
10958         }
10959         if(ce.title){
10960             tipTitle.update(ce.title);
10961             tipTitle.show();
10962         }else{
10963             tipTitle.update('');
10964             tipTitle.hide();
10965         }
10966         el.dom.style.width  = tm.maxWidth+'px';
10967         //tipBody.dom.style.width = '';
10968         tipBodyText.update(o.text);
10969         var p = getPad(), w = ce.width;
10970         if(!w){
10971             var td = tipBodyText.dom;
10972             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10973             if(aw > tm.maxWidth){
10974                 w = tm.maxWidth;
10975             }else if(aw < tm.minWidth){
10976                 w = tm.minWidth;
10977             }else{
10978                 w = aw;
10979             }
10980         }
10981         //tipBody.setWidth(w);
10982         el.setWidth(parseInt(w, 10) + p);
10983         if(ce.autoHide === false){
10984             close.setDisplayed(true);
10985             if(dd){
10986                 dd.unlock();
10987             }
10988         }else{
10989             close.setDisplayed(false);
10990             if(dd){
10991                 dd.lock();
10992             }
10993         }
10994         if(xy){
10995             el.avoidY = xy[1]-18;
10996             el.setXY(xy);
10997         }
10998         if(tm.animate){
10999             el.setOpacity(.1);
11000             el.setStyle("visibility", "visible");
11001             el.fadeIn({callback: afterShow});
11002         }else{
11003             afterShow();
11004         }
11005     };
11006     
11007     var afterShow = function(){
11008         if(ce){
11009             el.show();
11010             esc.enable();
11011             if(tm.autoDismiss && ce.autoHide !== false){
11012                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11013             }
11014         }
11015     };
11016     
11017     var hide = function(noanim){
11018         clearTimeout(dismissProc);
11019         clearTimeout(hideProc);
11020         ce = null;
11021         if(el.isVisible()){
11022             esc.disable();
11023             if(noanim !== true && tm.animate){
11024                 el.fadeOut({callback: afterHide});
11025             }else{
11026                 afterHide();
11027             } 
11028         }
11029     };
11030     
11031     var afterHide = function(){
11032         el.hide();
11033         if(removeCls){
11034             el.removeClass(removeCls);
11035             removeCls = null;
11036         }
11037     };
11038     
11039     return {
11040         /**
11041         * @cfg {Number} minWidth
11042         * The minimum width of the quick tip (defaults to 40)
11043         */
11044        minWidth : 40,
11045         /**
11046         * @cfg {Number} maxWidth
11047         * The maximum width of the quick tip (defaults to 300)
11048         */
11049        maxWidth : 300,
11050         /**
11051         * @cfg {Boolean} interceptTitles
11052         * True to automatically use the element's DOM title value if available (defaults to false)
11053         */
11054        interceptTitles : false,
11055         /**
11056         * @cfg {Boolean} trackMouse
11057         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11058         */
11059        trackMouse : false,
11060         /**
11061         * @cfg {Boolean} hideOnClick
11062         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11063         */
11064        hideOnClick : true,
11065         /**
11066         * @cfg {Number} showDelay
11067         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11068         */
11069        showDelay : 500,
11070         /**
11071         * @cfg {Number} hideDelay
11072         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11073         */
11074        hideDelay : 200,
11075         /**
11076         * @cfg {Boolean} autoHide
11077         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11078         * Used in conjunction with hideDelay.
11079         */
11080        autoHide : true,
11081         /**
11082         * @cfg {Boolean}
11083         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11084         * (defaults to true).  Used in conjunction with autoDismissDelay.
11085         */
11086        autoDismiss : true,
11087         /**
11088         * @cfg {Number}
11089         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11090         */
11091        autoDismissDelay : 5000,
11092        /**
11093         * @cfg {Boolean} animate
11094         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11095         */
11096        animate : false,
11097
11098        /**
11099         * @cfg {String} title
11100         * Title text to display (defaults to '').  This can be any valid HTML markup.
11101         */
11102         title: '',
11103        /**
11104         * @cfg {String} text
11105         * Body text to display (defaults to '').  This can be any valid HTML markup.
11106         */
11107         text : '',
11108        /**
11109         * @cfg {String} cls
11110         * A CSS class to apply to the base quick tip element (defaults to '').
11111         */
11112         cls : '',
11113        /**
11114         * @cfg {Number} width
11115         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11116         * minWidth or maxWidth.
11117         */
11118         width : null,
11119
11120     /**
11121      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11122      * or display QuickTips in a page.
11123      */
11124        init : function(){
11125           tm = Roo.QuickTips;
11126           cfg = tm.tagConfig;
11127           if(!inited){
11128               if(!Roo.isReady){ // allow calling of init() before onReady
11129                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11130                   return;
11131               }
11132               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11133               el.fxDefaults = {stopFx: true};
11134               // maximum custom styling
11135               //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>');
11136               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>');              
11137               tipTitle = el.child('h3');
11138               tipTitle.enableDisplayMode("block");
11139               tipBody = el.child('div.x-tip-bd');
11140               tipBodyText = el.child('div.x-tip-bd-inner');
11141               //bdLeft = el.child('div.x-tip-bd-left');
11142               //bdRight = el.child('div.x-tip-bd-right');
11143               close = el.child('div.x-tip-close');
11144               close.enableDisplayMode("block");
11145               close.on("click", hide);
11146               var d = Roo.get(document);
11147               d.on("mousedown", onDown);
11148               d.on("mouseover", onOver);
11149               d.on("mouseout", onOut);
11150               d.on("mousemove", onMove);
11151               esc = d.addKeyListener(27, hide);
11152               esc.disable();
11153               if(Roo.dd.DD){
11154                   dd = el.initDD("default", null, {
11155                       onDrag : function(){
11156                           el.sync();  
11157                       }
11158                   });
11159                   dd.setHandleElId(tipTitle.id);
11160                   dd.lock();
11161               }
11162               inited = true;
11163           }
11164           this.enable(); 
11165        },
11166
11167     /**
11168      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11169      * are supported:
11170      * <pre>
11171 Property    Type                   Description
11172 ----------  ---------------------  ------------------------------------------------------------------------
11173 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11174      * </ul>
11175      * @param {Object} config The config object
11176      */
11177        register : function(config){
11178            var cs = config instanceof Array ? config : arguments;
11179            for(var i = 0, len = cs.length; i < len; i++) {
11180                var c = cs[i];
11181                var target = c.target;
11182                if(target){
11183                    if(target instanceof Array){
11184                        for(var j = 0, jlen = target.length; j < jlen; j++){
11185                            tagEls[target[j]] = c;
11186                        }
11187                    }else{
11188                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11189                    }
11190                }
11191            }
11192        },
11193
11194     /**
11195      * Removes this quick tip from its element and destroys it.
11196      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11197      */
11198        unregister : function(el){
11199            delete tagEls[Roo.id(el)];
11200        },
11201
11202     /**
11203      * Enable this quick tip.
11204      */
11205        enable : function(){
11206            if(inited && disabled){
11207                locks.pop();
11208                if(locks.length < 1){
11209                    disabled = false;
11210                }
11211            }
11212        },
11213
11214     /**
11215      * Disable this quick tip.
11216      */
11217        disable : function(){
11218           disabled = true;
11219           clearTimeout(showProc);
11220           clearTimeout(hideProc);
11221           clearTimeout(dismissProc);
11222           if(ce){
11223               hide(true);
11224           }
11225           locks.push(1);
11226        },
11227
11228     /**
11229      * Returns true if the quick tip is enabled, else false.
11230      */
11231        isEnabled : function(){
11232             return !disabled;
11233        },
11234
11235         // private
11236        tagConfig : {
11237            namespace : "roo", // was ext?? this may break..
11238            alt_namespace : "ext",
11239            attribute : "qtip",
11240            width : "width",
11241            target : "target",
11242            title : "qtitle",
11243            hide : "hide",
11244            cls : "qclass"
11245        }
11246    };
11247 }();
11248
11249 // backwards compat
11250 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11251  * Based on:
11252  * Ext JS Library 1.1.1
11253  * Copyright(c) 2006-2007, Ext JS, LLC.
11254  *
11255  * Originally Released Under LGPL - original licence link has changed is not relivant.
11256  *
11257  * Fork - LGPL
11258  * <script type="text/javascript">
11259  */
11260  
11261
11262 /**
11263  * @class Roo.tree.TreePanel
11264  * @extends Roo.data.Tree
11265  * @cfg {Roo.tree.TreeNode} root The root node
11266  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11267  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11268  * @cfg {Boolean} enableDD true to enable drag and drop
11269  * @cfg {Boolean} enableDrag true to enable just drag
11270  * @cfg {Boolean} enableDrop true to enable just drop
11271  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11272  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11273  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11274  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11275  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11276  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11277  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11278  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11279  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11280  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11281  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11282  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11283  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11284  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11285  * @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>
11286  * @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>
11287  * 
11288  * @constructor
11289  * @param {String/HTMLElement/Element} el The container element
11290  * @param {Object} config
11291  */
11292 Roo.tree.TreePanel = function(el, config){
11293     var root = false;
11294     var loader = false;
11295     if (config.root) {
11296         root = config.root;
11297         delete config.root;
11298     }
11299     if (config.loader) {
11300         loader = config.loader;
11301         delete config.loader;
11302     }
11303     
11304     Roo.apply(this, config);
11305     Roo.tree.TreePanel.superclass.constructor.call(this);
11306     this.el = Roo.get(el);
11307     this.el.addClass('x-tree');
11308     //console.log(root);
11309     if (root) {
11310         this.setRootNode( Roo.factory(root, Roo.tree));
11311     }
11312     if (loader) {
11313         this.loader = Roo.factory(loader, Roo.tree);
11314     }
11315    /**
11316     * Read-only. The id of the container element becomes this TreePanel's id.
11317     */
11318     this.id = this.el.id;
11319     this.addEvents({
11320         /**
11321         * @event beforeload
11322         * Fires before a node is loaded, return false to cancel
11323         * @param {Node} node The node being loaded
11324         */
11325         "beforeload" : true,
11326         /**
11327         * @event load
11328         * Fires when a node is loaded
11329         * @param {Node} node The node that was loaded
11330         */
11331         "load" : true,
11332         /**
11333         * @event textchange
11334         * Fires when the text for a node is changed
11335         * @param {Node} node The node
11336         * @param {String} text The new text
11337         * @param {String} oldText The old text
11338         */
11339         "textchange" : true,
11340         /**
11341         * @event beforeexpand
11342         * Fires before a node is expanded, return false to cancel.
11343         * @param {Node} node The node
11344         * @param {Boolean} deep
11345         * @param {Boolean} anim
11346         */
11347         "beforeexpand" : true,
11348         /**
11349         * @event beforecollapse
11350         * Fires before a node is collapsed, return false to cancel.
11351         * @param {Node} node The node
11352         * @param {Boolean} deep
11353         * @param {Boolean} anim
11354         */
11355         "beforecollapse" : true,
11356         /**
11357         * @event expand
11358         * Fires when a node is expanded
11359         * @param {Node} node The node
11360         */
11361         "expand" : true,
11362         /**
11363         * @event disabledchange
11364         * Fires when the disabled status of a node changes
11365         * @param {Node} node The node
11366         * @param {Boolean} disabled
11367         */
11368         "disabledchange" : true,
11369         /**
11370         * @event collapse
11371         * Fires when a node is collapsed
11372         * @param {Node} node The node
11373         */
11374         "collapse" : true,
11375         /**
11376         * @event beforeclick
11377         * Fires before click processing on a node. Return false to cancel the default action.
11378         * @param {Node} node The node
11379         * @param {Roo.EventObject} e The event object
11380         */
11381         "beforeclick":true,
11382         /**
11383         * @event checkchange
11384         * Fires when a node with a checkbox's checked property changes
11385         * @param {Node} this This node
11386         * @param {Boolean} checked
11387         */
11388         "checkchange":true,
11389         /**
11390         * @event click
11391         * Fires when a node is clicked
11392         * @param {Node} node The node
11393         * @param {Roo.EventObject} e The event object
11394         */
11395         "click":true,
11396         /**
11397         * @event dblclick
11398         * Fires when a node is double clicked
11399         * @param {Node} node The node
11400         * @param {Roo.EventObject} e The event object
11401         */
11402         "dblclick":true,
11403         /**
11404         * @event contextmenu
11405         * Fires when a node is right clicked
11406         * @param {Node} node The node
11407         * @param {Roo.EventObject} e The event object
11408         */
11409         "contextmenu":true,
11410         /**
11411         * @event beforechildrenrendered
11412         * Fires right before the child nodes for a node are rendered
11413         * @param {Node} node The node
11414         */
11415         "beforechildrenrendered":true,
11416         /**
11417         * @event startdrag
11418         * Fires when a node starts being dragged
11419         * @param {Roo.tree.TreePanel} this
11420         * @param {Roo.tree.TreeNode} node
11421         * @param {event} e The raw browser event
11422         */ 
11423        "startdrag" : true,
11424        /**
11425         * @event enddrag
11426         * Fires when a drag operation is complete
11427         * @param {Roo.tree.TreePanel} this
11428         * @param {Roo.tree.TreeNode} node
11429         * @param {event} e The raw browser event
11430         */
11431        "enddrag" : true,
11432        /**
11433         * @event dragdrop
11434         * Fires when a dragged node is dropped on a valid DD target
11435         * @param {Roo.tree.TreePanel} this
11436         * @param {Roo.tree.TreeNode} node
11437         * @param {DD} dd The dd it was dropped on
11438         * @param {event} e The raw browser event
11439         */
11440        "dragdrop" : true,
11441        /**
11442         * @event beforenodedrop
11443         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11444         * passed to handlers has the following properties:<br />
11445         * <ul style="padding:5px;padding-left:16px;">
11446         * <li>tree - The TreePanel</li>
11447         * <li>target - The node being targeted for the drop</li>
11448         * <li>data - The drag data from the drag source</li>
11449         * <li>point - The point of the drop - append, above or below</li>
11450         * <li>source - The drag source</li>
11451         * <li>rawEvent - Raw mouse event</li>
11452         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11453         * to be inserted by setting them on this object.</li>
11454         * <li>cancel - Set this to true to cancel the drop.</li>
11455         * </ul>
11456         * @param {Object} dropEvent
11457         */
11458        "beforenodedrop" : true,
11459        /**
11460         * @event nodedrop
11461         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11462         * passed to handlers has the following properties:<br />
11463         * <ul style="padding:5px;padding-left:16px;">
11464         * <li>tree - The TreePanel</li>
11465         * <li>target - The node being targeted for the drop</li>
11466         * <li>data - The drag data from the drag source</li>
11467         * <li>point - The point of the drop - append, above or below</li>
11468         * <li>source - The drag source</li>
11469         * <li>rawEvent - Raw mouse event</li>
11470         * <li>dropNode - Dropped node(s).</li>
11471         * </ul>
11472         * @param {Object} dropEvent
11473         */
11474        "nodedrop" : true,
11475         /**
11476         * @event nodedragover
11477         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11478         * passed to handlers has the following properties:<br />
11479         * <ul style="padding:5px;padding-left:16px;">
11480         * <li>tree - The TreePanel</li>
11481         * <li>target - The node being targeted for the drop</li>
11482         * <li>data - The drag data from the drag source</li>
11483         * <li>point - The point of the drop - append, above or below</li>
11484         * <li>source - The drag source</li>
11485         * <li>rawEvent - Raw mouse event</li>
11486         * <li>dropNode - Drop node(s) provided by the source.</li>
11487         * <li>cancel - Set this to true to signal drop not allowed.</li>
11488         * </ul>
11489         * @param {Object} dragOverEvent
11490         */
11491        "nodedragover" : true,
11492        /**
11493         * @event appendnode
11494         * Fires when append node to the tree
11495         * @param {Roo.tree.TreePanel} this
11496         * @param {Roo.tree.TreeNode} node
11497         * @param {Number} index The index of the newly appended node
11498         */
11499        "appendnode" : true
11500         
11501     });
11502     if(this.singleExpand){
11503        this.on("beforeexpand", this.restrictExpand, this);
11504     }
11505     if (this.editor) {
11506         this.editor.tree = this;
11507         this.editor = Roo.factory(this.editor, Roo.tree);
11508     }
11509     
11510     if (this.selModel) {
11511         this.selModel = Roo.factory(this.selModel, Roo.tree);
11512     }
11513    
11514 };
11515 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11516     rootVisible : true,
11517     animate: Roo.enableFx,
11518     lines : true,
11519     enableDD : false,
11520     hlDrop : Roo.enableFx,
11521   
11522     renderer: false,
11523     
11524     rendererTip: false,
11525     // private
11526     restrictExpand : function(node){
11527         var p = node.parentNode;
11528         if(p){
11529             if(p.expandedChild && p.expandedChild.parentNode == p){
11530                 p.expandedChild.collapse();
11531             }
11532             p.expandedChild = node;
11533         }
11534     },
11535
11536     // private override
11537     setRootNode : function(node){
11538         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11539         if(!this.rootVisible){
11540             node.ui = new Roo.tree.RootTreeNodeUI(node);
11541         }
11542         return node;
11543     },
11544
11545     /**
11546      * Returns the container element for this TreePanel
11547      */
11548     getEl : function(){
11549         return this.el;
11550     },
11551
11552     /**
11553      * Returns the default TreeLoader for this TreePanel
11554      */
11555     getLoader : function(){
11556         return this.loader;
11557     },
11558
11559     /**
11560      * Expand all nodes
11561      */
11562     expandAll : function(){
11563         this.root.expand(true);
11564     },
11565
11566     /**
11567      * Collapse all nodes
11568      */
11569     collapseAll : function(){
11570         this.root.collapse(true);
11571     },
11572
11573     /**
11574      * Returns the selection model used by this TreePanel
11575      */
11576     getSelectionModel : function(){
11577         if(!this.selModel){
11578             this.selModel = new Roo.tree.DefaultSelectionModel();
11579         }
11580         return this.selModel;
11581     },
11582
11583     /**
11584      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11585      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11586      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11587      * @return {Array}
11588      */
11589     getChecked : function(a, startNode){
11590         startNode = startNode || this.root;
11591         var r = [];
11592         var f = function(){
11593             if(this.attributes.checked){
11594                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11595             }
11596         }
11597         startNode.cascade(f);
11598         return r;
11599     },
11600
11601     /**
11602      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11603      * @param {String} path
11604      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11605      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11606      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11607      */
11608     expandPath : function(path, attr, callback){
11609         attr = attr || "id";
11610         var keys = path.split(this.pathSeparator);
11611         var curNode = this.root;
11612         if(curNode.attributes[attr] != keys[1]){ // invalid root
11613             if(callback){
11614                 callback(false, null);
11615             }
11616             return;
11617         }
11618         var index = 1;
11619         var f = function(){
11620             if(++index == keys.length){
11621                 if(callback){
11622                     callback(true, curNode);
11623                 }
11624                 return;
11625             }
11626             var c = curNode.findChild(attr, keys[index]);
11627             if(!c){
11628                 if(callback){
11629                     callback(false, curNode);
11630                 }
11631                 return;
11632             }
11633             curNode = c;
11634             c.expand(false, false, f);
11635         };
11636         curNode.expand(false, false, f);
11637     },
11638
11639     /**
11640      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11641      * @param {String} path
11642      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11643      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11644      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11645      */
11646     selectPath : function(path, attr, callback){
11647         attr = attr || "id";
11648         var keys = path.split(this.pathSeparator);
11649         var v = keys.pop();
11650         if(keys.length > 0){
11651             var f = function(success, node){
11652                 if(success && node){
11653                     var n = node.findChild(attr, v);
11654                     if(n){
11655                         n.select();
11656                         if(callback){
11657                             callback(true, n);
11658                         }
11659                     }else if(callback){
11660                         callback(false, n);
11661                     }
11662                 }else{
11663                     if(callback){
11664                         callback(false, n);
11665                     }
11666                 }
11667             };
11668             this.expandPath(keys.join(this.pathSeparator), attr, f);
11669         }else{
11670             this.root.select();
11671             if(callback){
11672                 callback(true, this.root);
11673             }
11674         }
11675     },
11676
11677     getTreeEl : function(){
11678         return this.el;
11679     },
11680
11681     /**
11682      * Trigger rendering of this TreePanel
11683      */
11684     render : function(){
11685         if (this.innerCt) {
11686             return this; // stop it rendering more than once!!
11687         }
11688         
11689         this.innerCt = this.el.createChild({tag:"ul",
11690                cls:"x-tree-root-ct " +
11691                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11692
11693         if(this.containerScroll){
11694             Roo.dd.ScrollManager.register(this.el);
11695         }
11696         if((this.enableDD || this.enableDrop) && !this.dropZone){
11697            /**
11698             * The dropZone used by this tree if drop is enabled
11699             * @type Roo.tree.TreeDropZone
11700             */
11701              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11702                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11703            });
11704         }
11705         if((this.enableDD || this.enableDrag) && !this.dragZone){
11706            /**
11707             * The dragZone used by this tree if drag is enabled
11708             * @type Roo.tree.TreeDragZone
11709             */
11710             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11711                ddGroup: this.ddGroup || "TreeDD",
11712                scroll: this.ddScroll
11713            });
11714         }
11715         this.getSelectionModel().init(this);
11716         if (!this.root) {
11717             Roo.log("ROOT not set in tree");
11718             return this;
11719         }
11720         this.root.render();
11721         if(!this.rootVisible){
11722             this.root.renderChildren();
11723         }
11724         return this;
11725     }
11726 });/*
11727  * Based on:
11728  * Ext JS Library 1.1.1
11729  * Copyright(c) 2006-2007, Ext JS, LLC.
11730  *
11731  * Originally Released Under LGPL - original licence link has changed is not relivant.
11732  *
11733  * Fork - LGPL
11734  * <script type="text/javascript">
11735  */
11736  
11737
11738 /**
11739  * @class Roo.tree.DefaultSelectionModel
11740  * @extends Roo.util.Observable
11741  * The default single selection for a TreePanel.
11742  * @param {Object} cfg Configuration
11743  */
11744 Roo.tree.DefaultSelectionModel = function(cfg){
11745    this.selNode = null;
11746    
11747    
11748    
11749    this.addEvents({
11750        /**
11751         * @event selectionchange
11752         * Fires when the selected node changes
11753         * @param {DefaultSelectionModel} this
11754         * @param {TreeNode} node the new selection
11755         */
11756        "selectionchange" : true,
11757
11758        /**
11759         * @event beforeselect
11760         * Fires before the selected node changes, return false to cancel the change
11761         * @param {DefaultSelectionModel} this
11762         * @param {TreeNode} node the new selection
11763         * @param {TreeNode} node the old selection
11764         */
11765        "beforeselect" : true
11766    });
11767    
11768     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11769 };
11770
11771 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11772     init : function(tree){
11773         this.tree = tree;
11774         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11775         tree.on("click", this.onNodeClick, this);
11776     },
11777     
11778     onNodeClick : function(node, e){
11779         if (e.ctrlKey && this.selNode == node)  {
11780             this.unselect(node);
11781             return;
11782         }
11783         this.select(node);
11784     },
11785     
11786     /**
11787      * Select a node.
11788      * @param {TreeNode} node The node to select
11789      * @return {TreeNode} The selected node
11790      */
11791     select : function(node){
11792         var last = this.selNode;
11793         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11794             if(last){
11795                 last.ui.onSelectedChange(false);
11796             }
11797             this.selNode = node;
11798             node.ui.onSelectedChange(true);
11799             this.fireEvent("selectionchange", this, node, last);
11800         }
11801         return node;
11802     },
11803     
11804     /**
11805      * Deselect a node.
11806      * @param {TreeNode} node The node to unselect
11807      */
11808     unselect : function(node){
11809         if(this.selNode == node){
11810             this.clearSelections();
11811         }    
11812     },
11813     
11814     /**
11815      * Clear all selections
11816      */
11817     clearSelections : function(){
11818         var n = this.selNode;
11819         if(n){
11820             n.ui.onSelectedChange(false);
11821             this.selNode = null;
11822             this.fireEvent("selectionchange", this, null);
11823         }
11824         return n;
11825     },
11826     
11827     /**
11828      * Get the selected node
11829      * @return {TreeNode} The selected node
11830      */
11831     getSelectedNode : function(){
11832         return this.selNode;    
11833     },
11834     
11835     /**
11836      * Returns true if the node is selected
11837      * @param {TreeNode} node The node to check
11838      * @return {Boolean}
11839      */
11840     isSelected : function(node){
11841         return this.selNode == node;  
11842     },
11843
11844     /**
11845      * Selects the node above the selected node in the tree, intelligently walking the nodes
11846      * @return TreeNode The new selection
11847      */
11848     selectPrevious : function(){
11849         var s = this.selNode || this.lastSelNode;
11850         if(!s){
11851             return null;
11852         }
11853         var ps = s.previousSibling;
11854         if(ps){
11855             if(!ps.isExpanded() || ps.childNodes.length < 1){
11856                 return this.select(ps);
11857             } else{
11858                 var lc = ps.lastChild;
11859                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11860                     lc = lc.lastChild;
11861                 }
11862                 return this.select(lc);
11863             }
11864         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11865             return this.select(s.parentNode);
11866         }
11867         return null;
11868     },
11869
11870     /**
11871      * Selects the node above the selected node in the tree, intelligently walking the nodes
11872      * @return TreeNode The new selection
11873      */
11874     selectNext : function(){
11875         var s = this.selNode || this.lastSelNode;
11876         if(!s){
11877             return null;
11878         }
11879         if(s.firstChild && s.isExpanded()){
11880              return this.select(s.firstChild);
11881          }else if(s.nextSibling){
11882              return this.select(s.nextSibling);
11883          }else if(s.parentNode){
11884             var newS = null;
11885             s.parentNode.bubble(function(){
11886                 if(this.nextSibling){
11887                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11888                     return false;
11889                 }
11890             });
11891             return newS;
11892          }
11893         return null;
11894     },
11895
11896     onKeyDown : function(e){
11897         var s = this.selNode || this.lastSelNode;
11898         // undesirable, but required
11899         var sm = this;
11900         if(!s){
11901             return;
11902         }
11903         var k = e.getKey();
11904         switch(k){
11905              case e.DOWN:
11906                  e.stopEvent();
11907                  this.selectNext();
11908              break;
11909              case e.UP:
11910                  e.stopEvent();
11911                  this.selectPrevious();
11912              break;
11913              case e.RIGHT:
11914                  e.preventDefault();
11915                  if(s.hasChildNodes()){
11916                      if(!s.isExpanded()){
11917                          s.expand();
11918                      }else if(s.firstChild){
11919                          this.select(s.firstChild, e);
11920                      }
11921                  }
11922              break;
11923              case e.LEFT:
11924                  e.preventDefault();
11925                  if(s.hasChildNodes() && s.isExpanded()){
11926                      s.collapse();
11927                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11928                      this.select(s.parentNode, e);
11929                  }
11930              break;
11931         };
11932     }
11933 });
11934
11935 /**
11936  * @class Roo.tree.MultiSelectionModel
11937  * @extends Roo.util.Observable
11938  * Multi selection for a TreePanel.
11939  * @param {Object} cfg Configuration
11940  */
11941 Roo.tree.MultiSelectionModel = function(){
11942    this.selNodes = [];
11943    this.selMap = {};
11944    this.addEvents({
11945        /**
11946         * @event selectionchange
11947         * Fires when the selected nodes change
11948         * @param {MultiSelectionModel} this
11949         * @param {Array} nodes Array of the selected nodes
11950         */
11951        "selectionchange" : true
11952    });
11953    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11954    
11955 };
11956
11957 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11958     init : function(tree){
11959         this.tree = tree;
11960         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11961         tree.on("click", this.onNodeClick, this);
11962     },
11963     
11964     onNodeClick : function(node, e){
11965         this.select(node, e, e.ctrlKey);
11966     },
11967     
11968     /**
11969      * Select a node.
11970      * @param {TreeNode} node The node to select
11971      * @param {EventObject} e (optional) An event associated with the selection
11972      * @param {Boolean} keepExisting True to retain existing selections
11973      * @return {TreeNode} The selected node
11974      */
11975     select : function(node, e, keepExisting){
11976         if(keepExisting !== true){
11977             this.clearSelections(true);
11978         }
11979         if(this.isSelected(node)){
11980             this.lastSelNode = node;
11981             return node;
11982         }
11983         this.selNodes.push(node);
11984         this.selMap[node.id] = node;
11985         this.lastSelNode = node;
11986         node.ui.onSelectedChange(true);
11987         this.fireEvent("selectionchange", this, this.selNodes);
11988         return node;
11989     },
11990     
11991     /**
11992      * Deselect a node.
11993      * @param {TreeNode} node The node to unselect
11994      */
11995     unselect : function(node){
11996         if(this.selMap[node.id]){
11997             node.ui.onSelectedChange(false);
11998             var sn = this.selNodes;
11999             var index = -1;
12000             if(sn.indexOf){
12001                 index = sn.indexOf(node);
12002             }else{
12003                 for(var i = 0, len = sn.length; i < len; i++){
12004                     if(sn[i] == node){
12005                         index = i;
12006                         break;
12007                     }
12008                 }
12009             }
12010             if(index != -1){
12011                 this.selNodes.splice(index, 1);
12012             }
12013             delete this.selMap[node.id];
12014             this.fireEvent("selectionchange", this, this.selNodes);
12015         }
12016     },
12017     
12018     /**
12019      * Clear all selections
12020      */
12021     clearSelections : function(suppressEvent){
12022         var sn = this.selNodes;
12023         if(sn.length > 0){
12024             for(var i = 0, len = sn.length; i < len; i++){
12025                 sn[i].ui.onSelectedChange(false);
12026             }
12027             this.selNodes = [];
12028             this.selMap = {};
12029             if(suppressEvent !== true){
12030                 this.fireEvent("selectionchange", this, this.selNodes);
12031             }
12032         }
12033     },
12034     
12035     /**
12036      * Returns true if the node is selected
12037      * @param {TreeNode} node The node to check
12038      * @return {Boolean}
12039      */
12040     isSelected : function(node){
12041         return this.selMap[node.id] ? true : false;  
12042     },
12043     
12044     /**
12045      * Returns an array of the selected nodes
12046      * @return {Array}
12047      */
12048     getSelectedNodes : function(){
12049         return this.selNodes;    
12050     },
12051
12052     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12053
12054     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12055
12056     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12057 });/*
12058  * Based on:
12059  * Ext JS Library 1.1.1
12060  * Copyright(c) 2006-2007, Ext JS, LLC.
12061  *
12062  * Originally Released Under LGPL - original licence link has changed is not relivant.
12063  *
12064  * Fork - LGPL
12065  * <script type="text/javascript">
12066  */
12067  
12068 /**
12069  * @class Roo.tree.TreeNode
12070  * @extends Roo.data.Node
12071  * @cfg {String} text The text for this node
12072  * @cfg {Boolean} expanded true to start the node expanded
12073  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12074  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12075  * @cfg {Boolean} disabled true to start the node disabled
12076  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12077  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12078  * @cfg {String} cls A css class to be added to the node
12079  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12080  * @cfg {String} href URL of the link used for the node (defaults to #)
12081  * @cfg {String} hrefTarget target frame for the link
12082  * @cfg {String} qtip An Ext QuickTip for the node
12083  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12084  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12085  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12086  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12087  * (defaults to undefined with no checkbox rendered)
12088  * @constructor
12089  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12090  */
12091 Roo.tree.TreeNode = function(attributes){
12092     attributes = attributes || {};
12093     if(typeof attributes == "string"){
12094         attributes = {text: attributes};
12095     }
12096     this.childrenRendered = false;
12097     this.rendered = false;
12098     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12099     this.expanded = attributes.expanded === true;
12100     this.isTarget = attributes.isTarget !== false;
12101     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12102     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12103
12104     /**
12105      * Read-only. The text for this node. To change it use setText().
12106      * @type String
12107      */
12108     this.text = attributes.text;
12109     /**
12110      * True if this node is disabled.
12111      * @type Boolean
12112      */
12113     this.disabled = attributes.disabled === true;
12114
12115     this.addEvents({
12116         /**
12117         * @event textchange
12118         * Fires when the text for this node is changed
12119         * @param {Node} this This node
12120         * @param {String} text The new text
12121         * @param {String} oldText The old text
12122         */
12123         "textchange" : true,
12124         /**
12125         * @event beforeexpand
12126         * Fires before this node is expanded, return false to cancel.
12127         * @param {Node} this This node
12128         * @param {Boolean} deep
12129         * @param {Boolean} anim
12130         */
12131         "beforeexpand" : true,
12132         /**
12133         * @event beforecollapse
12134         * Fires before this node is collapsed, return false to cancel.
12135         * @param {Node} this This node
12136         * @param {Boolean} deep
12137         * @param {Boolean} anim
12138         */
12139         "beforecollapse" : true,
12140         /**
12141         * @event expand
12142         * Fires when this node is expanded
12143         * @param {Node} this This node
12144         */
12145         "expand" : true,
12146         /**
12147         * @event disabledchange
12148         * Fires when the disabled status of this node changes
12149         * @param {Node} this This node
12150         * @param {Boolean} disabled
12151         */
12152         "disabledchange" : true,
12153         /**
12154         * @event collapse
12155         * Fires when this node is collapsed
12156         * @param {Node} this This node
12157         */
12158         "collapse" : true,
12159         /**
12160         * @event beforeclick
12161         * Fires before click processing. Return false to cancel the default action.
12162         * @param {Node} this This node
12163         * @param {Roo.EventObject} e The event object
12164         */
12165         "beforeclick":true,
12166         /**
12167         * @event checkchange
12168         * Fires when a node with a checkbox's checked property changes
12169         * @param {Node} this This node
12170         * @param {Boolean} checked
12171         */
12172         "checkchange":true,
12173         /**
12174         * @event click
12175         * Fires when this node is clicked
12176         * @param {Node} this This node
12177         * @param {Roo.EventObject} e The event object
12178         */
12179         "click":true,
12180         /**
12181         * @event dblclick
12182         * Fires when this node is double clicked
12183         * @param {Node} this This node
12184         * @param {Roo.EventObject} e The event object
12185         */
12186         "dblclick":true,
12187         /**
12188         * @event contextmenu
12189         * Fires when this node is right clicked
12190         * @param {Node} this This node
12191         * @param {Roo.EventObject} e The event object
12192         */
12193         "contextmenu":true,
12194         /**
12195         * @event beforechildrenrendered
12196         * Fires right before the child nodes for this node are rendered
12197         * @param {Node} this This node
12198         */
12199         "beforechildrenrendered":true
12200     });
12201
12202     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12203
12204     /**
12205      * Read-only. The UI for this node
12206      * @type TreeNodeUI
12207      */
12208     this.ui = new uiClass(this);
12209     
12210     // finally support items[]
12211     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12212         return;
12213     }
12214     
12215     
12216     Roo.each(this.attributes.items, function(c) {
12217         this.appendChild(Roo.factory(c,Roo.Tree));
12218     }, this);
12219     delete this.attributes.items;
12220     
12221     
12222     
12223 };
12224 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12225     preventHScroll: true,
12226     /**
12227      * Returns true if this node is expanded
12228      * @return {Boolean}
12229      */
12230     isExpanded : function(){
12231         return this.expanded;
12232     },
12233
12234     /**
12235      * Returns the UI object for this node
12236      * @return {TreeNodeUI}
12237      */
12238     getUI : function(){
12239         return this.ui;
12240     },
12241
12242     // private override
12243     setFirstChild : function(node){
12244         var of = this.firstChild;
12245         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12246         if(this.childrenRendered && of && node != of){
12247             of.renderIndent(true, true);
12248         }
12249         if(this.rendered){
12250             this.renderIndent(true, true);
12251         }
12252     },
12253
12254     // private override
12255     setLastChild : function(node){
12256         var ol = this.lastChild;
12257         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12258         if(this.childrenRendered && ol && node != ol){
12259             ol.renderIndent(true, true);
12260         }
12261         if(this.rendered){
12262             this.renderIndent(true, true);
12263         }
12264     },
12265
12266     // these methods are overridden to provide lazy rendering support
12267     // private override
12268     appendChild : function()
12269     {
12270         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12271         if(node && this.childrenRendered){
12272             node.render();
12273         }
12274         this.ui.updateExpandIcon();
12275         return node;
12276     },
12277
12278     // private override
12279     removeChild : function(node){
12280         this.ownerTree.getSelectionModel().unselect(node);
12281         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12282         // if it's been rendered remove dom node
12283         if(this.childrenRendered){
12284             node.ui.remove();
12285         }
12286         if(this.childNodes.length < 1){
12287             this.collapse(false, false);
12288         }else{
12289             this.ui.updateExpandIcon();
12290         }
12291         if(!this.firstChild) {
12292             this.childrenRendered = false;
12293         }
12294         return node;
12295     },
12296
12297     // private override
12298     insertBefore : function(node, refNode){
12299         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12300         if(newNode && refNode && this.childrenRendered){
12301             node.render();
12302         }
12303         this.ui.updateExpandIcon();
12304         return newNode;
12305     },
12306
12307     /**
12308      * Sets the text for this node
12309      * @param {String} text
12310      */
12311     setText : function(text){
12312         var oldText = this.text;
12313         this.text = text;
12314         this.attributes.text = text;
12315         if(this.rendered){ // event without subscribing
12316             this.ui.onTextChange(this, text, oldText);
12317         }
12318         this.fireEvent("textchange", this, text, oldText);
12319     },
12320
12321     /**
12322      * Triggers selection of this node
12323      */
12324     select : function(){
12325         this.getOwnerTree().getSelectionModel().select(this);
12326     },
12327
12328     /**
12329      * Triggers deselection of this node
12330      */
12331     unselect : function(){
12332         this.getOwnerTree().getSelectionModel().unselect(this);
12333     },
12334
12335     /**
12336      * Returns true if this node is selected
12337      * @return {Boolean}
12338      */
12339     isSelected : function(){
12340         return this.getOwnerTree().getSelectionModel().isSelected(this);
12341     },
12342
12343     /**
12344      * Expand this node.
12345      * @param {Boolean} deep (optional) True to expand all children as well
12346      * @param {Boolean} anim (optional) false to cancel the default animation
12347      * @param {Function} callback (optional) A callback to be called when
12348      * expanding this node completes (does not wait for deep expand to complete).
12349      * Called with 1 parameter, this node.
12350      */
12351     expand : function(deep, anim, callback){
12352         if(!this.expanded){
12353             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12354                 return;
12355             }
12356             if(!this.childrenRendered){
12357                 this.renderChildren();
12358             }
12359             this.expanded = true;
12360             
12361             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12362                 this.ui.animExpand(function(){
12363                     this.fireEvent("expand", this);
12364                     if(typeof callback == "function"){
12365                         callback(this);
12366                     }
12367                     if(deep === true){
12368                         this.expandChildNodes(true);
12369                     }
12370                 }.createDelegate(this));
12371                 return;
12372             }else{
12373                 this.ui.expand();
12374                 this.fireEvent("expand", this);
12375                 if(typeof callback == "function"){
12376                     callback(this);
12377                 }
12378             }
12379         }else{
12380            if(typeof callback == "function"){
12381                callback(this);
12382            }
12383         }
12384         if(deep === true){
12385             this.expandChildNodes(true);
12386         }
12387     },
12388
12389     isHiddenRoot : function(){
12390         return this.isRoot && !this.getOwnerTree().rootVisible;
12391     },
12392
12393     /**
12394      * Collapse this node.
12395      * @param {Boolean} deep (optional) True to collapse all children as well
12396      * @param {Boolean} anim (optional) false to cancel the default animation
12397      */
12398     collapse : function(deep, anim){
12399         if(this.expanded && !this.isHiddenRoot()){
12400             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12401                 return;
12402             }
12403             this.expanded = false;
12404             if((this.getOwnerTree().animate && anim !== false) || anim){
12405                 this.ui.animCollapse(function(){
12406                     this.fireEvent("collapse", this);
12407                     if(deep === true){
12408                         this.collapseChildNodes(true);
12409                     }
12410                 }.createDelegate(this));
12411                 return;
12412             }else{
12413                 this.ui.collapse();
12414                 this.fireEvent("collapse", this);
12415             }
12416         }
12417         if(deep === true){
12418             var cs = this.childNodes;
12419             for(var i = 0, len = cs.length; i < len; i++) {
12420                 cs[i].collapse(true, false);
12421             }
12422         }
12423     },
12424
12425     // private
12426     delayedExpand : function(delay){
12427         if(!this.expandProcId){
12428             this.expandProcId = this.expand.defer(delay, this);
12429         }
12430     },
12431
12432     // private
12433     cancelExpand : function(){
12434         if(this.expandProcId){
12435             clearTimeout(this.expandProcId);
12436         }
12437         this.expandProcId = false;
12438     },
12439
12440     /**
12441      * Toggles expanded/collapsed state of the node
12442      */
12443     toggle : function(){
12444         if(this.expanded){
12445             this.collapse();
12446         }else{
12447             this.expand();
12448         }
12449     },
12450
12451     /**
12452      * Ensures all parent nodes are expanded
12453      */
12454     ensureVisible : function(callback){
12455         var tree = this.getOwnerTree();
12456         tree.expandPath(this.parentNode.getPath(), false, function(){
12457             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12458             Roo.callback(callback);
12459         }.createDelegate(this));
12460     },
12461
12462     /**
12463      * Expand all child nodes
12464      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12465      */
12466     expandChildNodes : function(deep){
12467         var cs = this.childNodes;
12468         for(var i = 0, len = cs.length; i < len; i++) {
12469                 cs[i].expand(deep);
12470         }
12471     },
12472
12473     /**
12474      * Collapse all child nodes
12475      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12476      */
12477     collapseChildNodes : function(deep){
12478         var cs = this.childNodes;
12479         for(var i = 0, len = cs.length; i < len; i++) {
12480                 cs[i].collapse(deep);
12481         }
12482     },
12483
12484     /**
12485      * Disables this node
12486      */
12487     disable : function(){
12488         this.disabled = true;
12489         this.unselect();
12490         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12491             this.ui.onDisableChange(this, true);
12492         }
12493         this.fireEvent("disabledchange", this, true);
12494     },
12495
12496     /**
12497      * Enables this node
12498      */
12499     enable : function(){
12500         this.disabled = false;
12501         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12502             this.ui.onDisableChange(this, false);
12503         }
12504         this.fireEvent("disabledchange", this, false);
12505     },
12506
12507     // private
12508     renderChildren : function(suppressEvent){
12509         if(suppressEvent !== false){
12510             this.fireEvent("beforechildrenrendered", this);
12511         }
12512         var cs = this.childNodes;
12513         for(var i = 0, len = cs.length; i < len; i++){
12514             cs[i].render(true);
12515         }
12516         this.childrenRendered = true;
12517     },
12518
12519     // private
12520     sort : function(fn, scope){
12521         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12522         if(this.childrenRendered){
12523             var cs = this.childNodes;
12524             for(var i = 0, len = cs.length; i < len; i++){
12525                 cs[i].render(true);
12526             }
12527         }
12528     },
12529
12530     // private
12531     render : function(bulkRender){
12532         this.ui.render(bulkRender);
12533         if(!this.rendered){
12534             this.rendered = true;
12535             if(this.expanded){
12536                 this.expanded = false;
12537                 this.expand(false, false);
12538             }
12539         }
12540     },
12541
12542     // private
12543     renderIndent : function(deep, refresh){
12544         if(refresh){
12545             this.ui.childIndent = null;
12546         }
12547         this.ui.renderIndent();
12548         if(deep === true && this.childrenRendered){
12549             var cs = this.childNodes;
12550             for(var i = 0, len = cs.length; i < len; i++){
12551                 cs[i].renderIndent(true, refresh);
12552             }
12553         }
12554     }
12555 });/*
12556  * Based on:
12557  * Ext JS Library 1.1.1
12558  * Copyright(c) 2006-2007, Ext JS, LLC.
12559  *
12560  * Originally Released Under LGPL - original licence link has changed is not relivant.
12561  *
12562  * Fork - LGPL
12563  * <script type="text/javascript">
12564  */
12565  
12566 /**
12567  * @class Roo.tree.AsyncTreeNode
12568  * @extends Roo.tree.TreeNode
12569  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12570  * @constructor
12571  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12572  */
12573  Roo.tree.AsyncTreeNode = function(config){
12574     this.loaded = false;
12575     this.loading = false;
12576     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12577     /**
12578     * @event beforeload
12579     * Fires before this node is loaded, return false to cancel
12580     * @param {Node} this This node
12581     */
12582     this.addEvents({'beforeload':true, 'load': true});
12583     /**
12584     * @event load
12585     * Fires when this node is loaded
12586     * @param {Node} this This node
12587     */
12588     /**
12589      * The loader used by this node (defaults to using the tree's defined loader)
12590      * @type TreeLoader
12591      * @property loader
12592      */
12593 };
12594 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12595     expand : function(deep, anim, callback){
12596         if(this.loading){ // if an async load is already running, waiting til it's done
12597             var timer;
12598             var f = function(){
12599                 if(!this.loading){ // done loading
12600                     clearInterval(timer);
12601                     this.expand(deep, anim, callback);
12602                 }
12603             }.createDelegate(this);
12604             timer = setInterval(f, 200);
12605             return;
12606         }
12607         if(!this.loaded){
12608             if(this.fireEvent("beforeload", this) === false){
12609                 return;
12610             }
12611             this.loading = true;
12612             this.ui.beforeLoad(this);
12613             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12614             if(loader){
12615                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12616                 return;
12617             }
12618         }
12619         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12620     },
12621     
12622     /**
12623      * Returns true if this node is currently loading
12624      * @return {Boolean}
12625      */
12626     isLoading : function(){
12627         return this.loading;  
12628     },
12629     
12630     loadComplete : function(deep, anim, callback){
12631         this.loading = false;
12632         this.loaded = true;
12633         this.ui.afterLoad(this);
12634         this.fireEvent("load", this);
12635         this.expand(deep, anim, callback);
12636     },
12637     
12638     /**
12639      * Returns true if this node has been loaded
12640      * @return {Boolean}
12641      */
12642     isLoaded : function(){
12643         return this.loaded;
12644     },
12645     
12646     hasChildNodes : function(){
12647         if(!this.isLeaf() && !this.loaded){
12648             return true;
12649         }else{
12650             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12651         }
12652     },
12653
12654     /**
12655      * Trigger a reload for this node
12656      * @param {Function} callback
12657      */
12658     reload : function(callback){
12659         this.collapse(false, false);
12660         while(this.firstChild){
12661             this.removeChild(this.firstChild);
12662         }
12663         this.childrenRendered = false;
12664         this.loaded = false;
12665         if(this.isHiddenRoot()){
12666             this.expanded = false;
12667         }
12668         this.expand(false, false, callback);
12669     }
12670 });/*
12671  * Based on:
12672  * Ext JS Library 1.1.1
12673  * Copyright(c) 2006-2007, Ext JS, LLC.
12674  *
12675  * Originally Released Under LGPL - original licence link has changed is not relivant.
12676  *
12677  * Fork - LGPL
12678  * <script type="text/javascript">
12679  */
12680  
12681 /**
12682  * @class Roo.tree.TreeNodeUI
12683  * @constructor
12684  * @param {Object} node The node to render
12685  * The TreeNode UI implementation is separate from the
12686  * tree implementation. Unless you are customizing the tree UI,
12687  * you should never have to use this directly.
12688  */
12689 Roo.tree.TreeNodeUI = function(node){
12690     this.node = node;
12691     this.rendered = false;
12692     this.animating = false;
12693     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12694 };
12695
12696 Roo.tree.TreeNodeUI.prototype = {
12697     removeChild : function(node){
12698         if(this.rendered){
12699             this.ctNode.removeChild(node.ui.getEl());
12700         }
12701     },
12702
12703     beforeLoad : function(){
12704          this.addClass("x-tree-node-loading");
12705     },
12706
12707     afterLoad : function(){
12708          this.removeClass("x-tree-node-loading");
12709     },
12710
12711     onTextChange : function(node, text, oldText){
12712         if(this.rendered){
12713             this.textNode.innerHTML = text;
12714         }
12715     },
12716
12717     onDisableChange : function(node, state){
12718         this.disabled = state;
12719         if(state){
12720             this.addClass("x-tree-node-disabled");
12721         }else{
12722             this.removeClass("x-tree-node-disabled");
12723         }
12724     },
12725
12726     onSelectedChange : function(state){
12727         if(state){
12728             this.focus();
12729             this.addClass("x-tree-selected");
12730         }else{
12731             //this.blur();
12732             this.removeClass("x-tree-selected");
12733         }
12734     },
12735
12736     onMove : function(tree, node, oldParent, newParent, index, refNode){
12737         this.childIndent = null;
12738         if(this.rendered){
12739             var targetNode = newParent.ui.getContainer();
12740             if(!targetNode){//target not rendered
12741                 this.holder = document.createElement("div");
12742                 this.holder.appendChild(this.wrap);
12743                 return;
12744             }
12745             var insertBefore = refNode ? refNode.ui.getEl() : null;
12746             if(insertBefore){
12747                 targetNode.insertBefore(this.wrap, insertBefore);
12748             }else{
12749                 targetNode.appendChild(this.wrap);
12750             }
12751             this.node.renderIndent(true);
12752         }
12753     },
12754
12755     addClass : function(cls){
12756         if(this.elNode){
12757             Roo.fly(this.elNode).addClass(cls);
12758         }
12759     },
12760
12761     removeClass : function(cls){
12762         if(this.elNode){
12763             Roo.fly(this.elNode).removeClass(cls);
12764         }
12765     },
12766
12767     remove : function(){
12768         if(this.rendered){
12769             this.holder = document.createElement("div");
12770             this.holder.appendChild(this.wrap);
12771         }
12772     },
12773
12774     fireEvent : function(){
12775         return this.node.fireEvent.apply(this.node, arguments);
12776     },
12777
12778     initEvents : function(){
12779         this.node.on("move", this.onMove, this);
12780         var E = Roo.EventManager;
12781         var a = this.anchor;
12782
12783         var el = Roo.fly(a, '_treeui');
12784
12785         if(Roo.isOpera){ // opera render bug ignores the CSS
12786             el.setStyle("text-decoration", "none");
12787         }
12788
12789         el.on("click", this.onClick, this);
12790         el.on("dblclick", this.onDblClick, this);
12791
12792         if(this.checkbox){
12793             Roo.EventManager.on(this.checkbox,
12794                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12795         }
12796
12797         el.on("contextmenu", this.onContextMenu, this);
12798
12799         var icon = Roo.fly(this.iconNode);
12800         icon.on("click", this.onClick, this);
12801         icon.on("dblclick", this.onDblClick, this);
12802         icon.on("contextmenu", this.onContextMenu, this);
12803         E.on(this.ecNode, "click", this.ecClick, this, true);
12804
12805         if(this.node.disabled){
12806             this.addClass("x-tree-node-disabled");
12807         }
12808         if(this.node.hidden){
12809             this.addClass("x-tree-node-disabled");
12810         }
12811         var ot = this.node.getOwnerTree();
12812         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12813         if(dd && (!this.node.isRoot || ot.rootVisible)){
12814             Roo.dd.Registry.register(this.elNode, {
12815                 node: this.node,
12816                 handles: this.getDDHandles(),
12817                 isHandle: false
12818             });
12819         }
12820     },
12821
12822     getDDHandles : function(){
12823         return [this.iconNode, this.textNode];
12824     },
12825
12826     hide : function(){
12827         if(this.rendered){
12828             this.wrap.style.display = "none";
12829         }
12830     },
12831
12832     show : function(){
12833         if(this.rendered){
12834             this.wrap.style.display = "";
12835         }
12836     },
12837
12838     onContextMenu : function(e){
12839         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12840             e.preventDefault();
12841             this.focus();
12842             this.fireEvent("contextmenu", this.node, e);
12843         }
12844     },
12845
12846     onClick : function(e){
12847         if(this.dropping){
12848             e.stopEvent();
12849             return;
12850         }
12851         if(this.fireEvent("beforeclick", this.node, e) !== false){
12852             if(!this.disabled && this.node.attributes.href){
12853                 this.fireEvent("click", this.node, e);
12854                 return;
12855             }
12856             e.preventDefault();
12857             if(this.disabled){
12858                 return;
12859             }
12860
12861             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12862                 this.node.toggle();
12863             }
12864
12865             this.fireEvent("click", this.node, e);
12866         }else{
12867             e.stopEvent();
12868         }
12869     },
12870
12871     onDblClick : function(e){
12872         e.preventDefault();
12873         if(this.disabled){
12874             return;
12875         }
12876         if(this.checkbox){
12877             this.toggleCheck();
12878         }
12879         if(!this.animating && this.node.hasChildNodes()){
12880             this.node.toggle();
12881         }
12882         this.fireEvent("dblclick", this.node, e);
12883     },
12884
12885     onCheckChange : function(){
12886         var checked = this.checkbox.checked;
12887         this.node.attributes.checked = checked;
12888         this.fireEvent('checkchange', this.node, checked);
12889     },
12890
12891     ecClick : function(e){
12892         if(!this.animating && this.node.hasChildNodes()){
12893             this.node.toggle();
12894         }
12895     },
12896
12897     startDrop : function(){
12898         this.dropping = true;
12899     },
12900
12901     // delayed drop so the click event doesn't get fired on a drop
12902     endDrop : function(){
12903        setTimeout(function(){
12904            this.dropping = false;
12905        }.createDelegate(this), 50);
12906     },
12907
12908     expand : function(){
12909         this.updateExpandIcon();
12910         this.ctNode.style.display = "";
12911     },
12912
12913     focus : function(){
12914         if(!this.node.preventHScroll){
12915             try{this.anchor.focus();
12916             }catch(e){}
12917         }else if(!Roo.isIE){
12918             try{
12919                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12920                 var l = noscroll.scrollLeft;
12921                 this.anchor.focus();
12922                 noscroll.scrollLeft = l;
12923             }catch(e){}
12924         }
12925     },
12926
12927     toggleCheck : function(value){
12928         var cb = this.checkbox;
12929         if(cb){
12930             cb.checked = (value === undefined ? !cb.checked : value);
12931         }
12932     },
12933
12934     blur : function(){
12935         try{
12936             this.anchor.blur();
12937         }catch(e){}
12938     },
12939
12940     animExpand : function(callback){
12941         var ct = Roo.get(this.ctNode);
12942         ct.stopFx();
12943         if(!this.node.hasChildNodes()){
12944             this.updateExpandIcon();
12945             this.ctNode.style.display = "";
12946             Roo.callback(callback);
12947             return;
12948         }
12949         this.animating = true;
12950         this.updateExpandIcon();
12951
12952         ct.slideIn('t', {
12953            callback : function(){
12954                this.animating = false;
12955                Roo.callback(callback);
12956             },
12957             scope: this,
12958             duration: this.node.ownerTree.duration || .25
12959         });
12960     },
12961
12962     highlight : function(){
12963         var tree = this.node.getOwnerTree();
12964         Roo.fly(this.wrap).highlight(
12965             tree.hlColor || "C3DAF9",
12966             {endColor: tree.hlBaseColor}
12967         );
12968     },
12969
12970     collapse : function(){
12971         this.updateExpandIcon();
12972         this.ctNode.style.display = "none";
12973     },
12974
12975     animCollapse : function(callback){
12976         var ct = Roo.get(this.ctNode);
12977         ct.enableDisplayMode('block');
12978         ct.stopFx();
12979
12980         this.animating = true;
12981         this.updateExpandIcon();
12982
12983         ct.slideOut('t', {
12984             callback : function(){
12985                this.animating = false;
12986                Roo.callback(callback);
12987             },
12988             scope: this,
12989             duration: this.node.ownerTree.duration || .25
12990         });
12991     },
12992
12993     getContainer : function(){
12994         return this.ctNode;
12995     },
12996
12997     getEl : function(){
12998         return this.wrap;
12999     },
13000
13001     appendDDGhost : function(ghostNode){
13002         ghostNode.appendChild(this.elNode.cloneNode(true));
13003     },
13004
13005     getDDRepairXY : function(){
13006         return Roo.lib.Dom.getXY(this.iconNode);
13007     },
13008
13009     onRender : function(){
13010         this.render();
13011     },
13012
13013     render : function(bulkRender){
13014         var n = this.node, a = n.attributes;
13015         var targetNode = n.parentNode ?
13016               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13017
13018         if(!this.rendered){
13019             this.rendered = true;
13020
13021             this.renderElements(n, a, targetNode, bulkRender);
13022
13023             if(a.qtip){
13024                if(this.textNode.setAttributeNS){
13025                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13026                    if(a.qtipTitle){
13027                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13028                    }
13029                }else{
13030                    this.textNode.setAttribute("ext:qtip", a.qtip);
13031                    if(a.qtipTitle){
13032                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13033                    }
13034                }
13035             }else if(a.qtipCfg){
13036                 a.qtipCfg.target = Roo.id(this.textNode);
13037                 Roo.QuickTips.register(a.qtipCfg);
13038             }
13039             this.initEvents();
13040             if(!this.node.expanded){
13041                 this.updateExpandIcon();
13042             }
13043         }else{
13044             if(bulkRender === true) {
13045                 targetNode.appendChild(this.wrap);
13046             }
13047         }
13048     },
13049
13050     renderElements : function(n, a, targetNode, bulkRender)
13051     {
13052         // add some indent caching, this helps performance when rendering a large tree
13053         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13054         var t = n.getOwnerTree();
13055         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13056         if (typeof(n.attributes.html) != 'undefined') {
13057             txt = n.attributes.html;
13058         }
13059         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13060         var cb = typeof a.checked == 'boolean';
13061         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13062         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13063             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13064             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13065             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13066             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13067             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13068              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13069                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13070             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13071             "</li>"];
13072
13073         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13074             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13075                                 n.nextSibling.ui.getEl(), buf.join(""));
13076         }else{
13077             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13078         }
13079
13080         this.elNode = this.wrap.childNodes[0];
13081         this.ctNode = this.wrap.childNodes[1];
13082         var cs = this.elNode.childNodes;
13083         this.indentNode = cs[0];
13084         this.ecNode = cs[1];
13085         this.iconNode = cs[2];
13086         var index = 3;
13087         if(cb){
13088             this.checkbox = cs[3];
13089             index++;
13090         }
13091         this.anchor = cs[index];
13092         this.textNode = cs[index].firstChild;
13093     },
13094
13095     getAnchor : function(){
13096         return this.anchor;
13097     },
13098
13099     getTextEl : function(){
13100         return this.textNode;
13101     },
13102
13103     getIconEl : function(){
13104         return this.iconNode;
13105     },
13106
13107     isChecked : function(){
13108         return this.checkbox ? this.checkbox.checked : false;
13109     },
13110
13111     updateExpandIcon : function(){
13112         if(this.rendered){
13113             var n = this.node, c1, c2;
13114             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13115             var hasChild = n.hasChildNodes();
13116             if(hasChild){
13117                 if(n.expanded){
13118                     cls += "-minus";
13119                     c1 = "x-tree-node-collapsed";
13120                     c2 = "x-tree-node-expanded";
13121                 }else{
13122                     cls += "-plus";
13123                     c1 = "x-tree-node-expanded";
13124                     c2 = "x-tree-node-collapsed";
13125                 }
13126                 if(this.wasLeaf){
13127                     this.removeClass("x-tree-node-leaf");
13128                     this.wasLeaf = false;
13129                 }
13130                 if(this.c1 != c1 || this.c2 != c2){
13131                     Roo.fly(this.elNode).replaceClass(c1, c2);
13132                     this.c1 = c1; this.c2 = c2;
13133                 }
13134             }else{
13135                 // this changes non-leafs into leafs if they have no children.
13136                 // it's not very rational behaviour..
13137                 
13138                 if(!this.wasLeaf && this.node.leaf){
13139                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13140                     delete this.c1;
13141                     delete this.c2;
13142                     this.wasLeaf = true;
13143                 }
13144             }
13145             var ecc = "x-tree-ec-icon "+cls;
13146             if(this.ecc != ecc){
13147                 this.ecNode.className = ecc;
13148                 this.ecc = ecc;
13149             }
13150         }
13151     },
13152
13153     getChildIndent : function(){
13154         if(!this.childIndent){
13155             var buf = [];
13156             var p = this.node;
13157             while(p){
13158                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13159                     if(!p.isLast()) {
13160                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13161                     } else {
13162                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13163                     }
13164                 }
13165                 p = p.parentNode;
13166             }
13167             this.childIndent = buf.join("");
13168         }
13169         return this.childIndent;
13170     },
13171
13172     renderIndent : function(){
13173         if(this.rendered){
13174             var indent = "";
13175             var p = this.node.parentNode;
13176             if(p){
13177                 indent = p.ui.getChildIndent();
13178             }
13179             if(this.indentMarkup != indent){ // don't rerender if not required
13180                 this.indentNode.innerHTML = indent;
13181                 this.indentMarkup = indent;
13182             }
13183             this.updateExpandIcon();
13184         }
13185     }
13186 };
13187
13188 Roo.tree.RootTreeNodeUI = function(){
13189     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13190 };
13191 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13192     render : function(){
13193         if(!this.rendered){
13194             var targetNode = this.node.ownerTree.innerCt.dom;
13195             this.node.expanded = true;
13196             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13197             this.wrap = this.ctNode = targetNode.firstChild;
13198         }
13199     },
13200     collapse : function(){
13201     },
13202     expand : function(){
13203     }
13204 });/*
13205  * Based on:
13206  * Ext JS Library 1.1.1
13207  * Copyright(c) 2006-2007, Ext JS, LLC.
13208  *
13209  * Originally Released Under LGPL - original licence link has changed is not relivant.
13210  *
13211  * Fork - LGPL
13212  * <script type="text/javascript">
13213  */
13214 /**
13215  * @class Roo.tree.TreeLoader
13216  * @extends Roo.util.Observable
13217  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13218  * nodes from a specified URL. The response must be a javascript Array definition
13219  * who's elements are node definition objects. eg:
13220  * <pre><code>
13221 {  success : true,
13222    data :      [
13223    
13224     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13225     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13226     ]
13227 }
13228
13229
13230 </code></pre>
13231  * <br><br>
13232  * The old style respose with just an array is still supported, but not recommended.
13233  * <br><br>
13234  *
13235  * A server request is sent, and child nodes are loaded only when a node is expanded.
13236  * The loading node's id is passed to the server under the parameter name "node" to
13237  * enable the server to produce the correct child nodes.
13238  * <br><br>
13239  * To pass extra parameters, an event handler may be attached to the "beforeload"
13240  * event, and the parameters specified in the TreeLoader's baseParams property:
13241  * <pre><code>
13242     myTreeLoader.on("beforeload", function(treeLoader, node) {
13243         this.baseParams.category = node.attributes.category;
13244     }, this);
13245     
13246 </code></pre>
13247  *
13248  * This would pass an HTTP parameter called "category" to the server containing
13249  * the value of the Node's "category" attribute.
13250  * @constructor
13251  * Creates a new Treeloader.
13252  * @param {Object} config A config object containing config properties.
13253  */
13254 Roo.tree.TreeLoader = function(config){
13255     this.baseParams = {};
13256     this.requestMethod = "POST";
13257     Roo.apply(this, config);
13258
13259     this.addEvents({
13260     
13261         /**
13262          * @event beforeload
13263          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13264          * @param {Object} This TreeLoader object.
13265          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13266          * @param {Object} callback The callback function specified in the {@link #load} call.
13267          */
13268         beforeload : true,
13269         /**
13270          * @event load
13271          * Fires when the node has been successfuly loaded.
13272          * @param {Object} This TreeLoader object.
13273          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13274          * @param {Object} response The response object containing the data from the server.
13275          */
13276         load : true,
13277         /**
13278          * @event loadexception
13279          * Fires if the network request failed.
13280          * @param {Object} This TreeLoader object.
13281          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13282          * @param {Object} response The response object containing the data from the server.
13283          */
13284         loadexception : true,
13285         /**
13286          * @event create
13287          * Fires before a node is created, enabling you to return custom Node types 
13288          * @param {Object} This TreeLoader object.
13289          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13290          */
13291         create : true
13292     });
13293
13294     Roo.tree.TreeLoader.superclass.constructor.call(this);
13295 };
13296
13297 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13298     /**
13299     * @cfg {String} dataUrl The URL from which to request a Json string which
13300     * specifies an array of node definition object representing the child nodes
13301     * to be loaded.
13302     */
13303     /**
13304     * @cfg {String} requestMethod either GET or POST
13305     * defaults to POST (due to BC)
13306     * to be loaded.
13307     */
13308     /**
13309     * @cfg {Object} baseParams (optional) An object containing properties which
13310     * specify HTTP parameters to be passed to each request for child nodes.
13311     */
13312     /**
13313     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13314     * created by this loader. If the attributes sent by the server have an attribute in this object,
13315     * they take priority.
13316     */
13317     /**
13318     * @cfg {Object} uiProviders (optional) An object containing properties which
13319     * 
13320     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13321     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13322     * <i>uiProvider</i> attribute of a returned child node is a string rather
13323     * than a reference to a TreeNodeUI implementation, this that string value
13324     * is used as a property name in the uiProviders object. You can define the provider named
13325     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13326     */
13327     uiProviders : {},
13328
13329     /**
13330     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13331     * child nodes before loading.
13332     */
13333     clearOnLoad : true,
13334
13335     /**
13336     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13337     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13338     * Grid query { data : [ .....] }
13339     */
13340     
13341     root : false,
13342      /**
13343     * @cfg {String} queryParam (optional) 
13344     * Name of the query as it will be passed on the querystring (defaults to 'node')
13345     * eg. the request will be ?node=[id]
13346     */
13347     
13348     
13349     queryParam: false,
13350     
13351     /**
13352      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13353      * This is called automatically when a node is expanded, but may be used to reload
13354      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13355      * @param {Roo.tree.TreeNode} node
13356      * @param {Function} callback
13357      */
13358     load : function(node, callback){
13359         if(this.clearOnLoad){
13360             while(node.firstChild){
13361                 node.removeChild(node.firstChild);
13362             }
13363         }
13364         if(node.attributes.children){ // preloaded json children
13365             var cs = node.attributes.children;
13366             for(var i = 0, len = cs.length; i < len; i++){
13367                 node.appendChild(this.createNode(cs[i]));
13368             }
13369             if(typeof callback == "function"){
13370                 callback();
13371             }
13372         }else if(this.dataUrl){
13373             this.requestData(node, callback);
13374         }
13375     },
13376
13377     getParams: function(node){
13378         var buf = [], bp = this.baseParams;
13379         for(var key in bp){
13380             if(typeof bp[key] != "function"){
13381                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13382             }
13383         }
13384         var n = this.queryParam === false ? 'node' : this.queryParam;
13385         buf.push(n + "=", encodeURIComponent(node.id));
13386         return buf.join("");
13387     },
13388
13389     requestData : function(node, callback){
13390         if(this.fireEvent("beforeload", this, node, callback) !== false){
13391             this.transId = Roo.Ajax.request({
13392                 method:this.requestMethod,
13393                 url: this.dataUrl||this.url,
13394                 success: this.handleResponse,
13395                 failure: this.handleFailure,
13396                 scope: this,
13397                 argument: {callback: callback, node: node},
13398                 params: this.getParams(node)
13399             });
13400         }else{
13401             // if the load is cancelled, make sure we notify
13402             // the node that we are done
13403             if(typeof callback == "function"){
13404                 callback();
13405             }
13406         }
13407     },
13408
13409     isLoading : function(){
13410         return this.transId ? true : false;
13411     },
13412
13413     abort : function(){
13414         if(this.isLoading()){
13415             Roo.Ajax.abort(this.transId);
13416         }
13417     },
13418
13419     // private
13420     createNode : function(attr)
13421     {
13422         // apply baseAttrs, nice idea Corey!
13423         if(this.baseAttrs){
13424             Roo.applyIf(attr, this.baseAttrs);
13425         }
13426         if(this.applyLoader !== false){
13427             attr.loader = this;
13428         }
13429         // uiProvider = depreciated..
13430         
13431         if(typeof(attr.uiProvider) == 'string'){
13432            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13433                 /**  eval:var:attr */ eval(attr.uiProvider);
13434         }
13435         if(typeof(this.uiProviders['default']) != 'undefined') {
13436             attr.uiProvider = this.uiProviders['default'];
13437         }
13438         
13439         this.fireEvent('create', this, attr);
13440         
13441         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13442         return(attr.leaf ?
13443                         new Roo.tree.TreeNode(attr) :
13444                         new Roo.tree.AsyncTreeNode(attr));
13445     },
13446
13447     processResponse : function(response, node, callback)
13448     {
13449         var json = response.responseText;
13450         try {
13451             
13452             var o = Roo.decode(json);
13453             
13454             if (this.root === false && typeof(o.success) != undefined) {
13455                 this.root = 'data'; // the default behaviour for list like data..
13456                 }
13457                 
13458             if (this.root !== false &&  !o.success) {
13459                 // it's a failure condition.
13460                 var a = response.argument;
13461                 this.fireEvent("loadexception", this, a.node, response);
13462                 Roo.log("Load failed - should have a handler really");
13463                 return;
13464             }
13465             
13466             
13467             
13468             if (this.root !== false) {
13469                  o = o[this.root];
13470             }
13471             
13472             for(var i = 0, len = o.length; i < len; i++){
13473                 var n = this.createNode(o[i]);
13474                 if(n){
13475                     node.appendChild(n);
13476                 }
13477             }
13478             if(typeof callback == "function"){
13479                 callback(this, node);
13480             }
13481         }catch(e){
13482             this.handleFailure(response);
13483         }
13484     },
13485
13486     handleResponse : function(response){
13487         this.transId = false;
13488         var a = response.argument;
13489         this.processResponse(response, a.node, a.callback);
13490         this.fireEvent("load", this, a.node, response);
13491     },
13492
13493     handleFailure : function(response)
13494     {
13495         // should handle failure better..
13496         this.transId = false;
13497         var a = response.argument;
13498         this.fireEvent("loadexception", this, a.node, response);
13499         if(typeof a.callback == "function"){
13500             a.callback(this, a.node);
13501         }
13502     }
13503 });/*
13504  * Based on:
13505  * Ext JS Library 1.1.1
13506  * Copyright(c) 2006-2007, Ext JS, LLC.
13507  *
13508  * Originally Released Under LGPL - original licence link has changed is not relivant.
13509  *
13510  * Fork - LGPL
13511  * <script type="text/javascript">
13512  */
13513
13514 /**
13515 * @class Roo.tree.TreeFilter
13516 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13517 * @param {TreePanel} tree
13518 * @param {Object} config (optional)
13519  */
13520 Roo.tree.TreeFilter = function(tree, config){
13521     this.tree = tree;
13522     this.filtered = {};
13523     Roo.apply(this, config);
13524 };
13525
13526 Roo.tree.TreeFilter.prototype = {
13527     clearBlank:false,
13528     reverse:false,
13529     autoClear:false,
13530     remove:false,
13531
13532      /**
13533      * Filter the data by a specific attribute.
13534      * @param {String/RegExp} value Either string that the attribute value
13535      * should start with or a RegExp to test against the attribute
13536      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13537      * @param {TreeNode} startNode (optional) The node to start the filter at.
13538      */
13539     filter : function(value, attr, startNode){
13540         attr = attr || "text";
13541         var f;
13542         if(typeof value == "string"){
13543             var vlen = value.length;
13544             // auto clear empty filter
13545             if(vlen == 0 && this.clearBlank){
13546                 this.clear();
13547                 return;
13548             }
13549             value = value.toLowerCase();
13550             f = function(n){
13551                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13552             };
13553         }else if(value.exec){ // regex?
13554             f = function(n){
13555                 return value.test(n.attributes[attr]);
13556             };
13557         }else{
13558             throw 'Illegal filter type, must be string or regex';
13559         }
13560         this.filterBy(f, null, startNode);
13561         },
13562
13563     /**
13564      * Filter by a function. The passed function will be called with each
13565      * node in the tree (or from the startNode). If the function returns true, the node is kept
13566      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13567      * @param {Function} fn The filter function
13568      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13569      */
13570     filterBy : function(fn, scope, startNode){
13571         startNode = startNode || this.tree.root;
13572         if(this.autoClear){
13573             this.clear();
13574         }
13575         var af = this.filtered, rv = this.reverse;
13576         var f = function(n){
13577             if(n == startNode){
13578                 return true;
13579             }
13580             if(af[n.id]){
13581                 return false;
13582             }
13583             var m = fn.call(scope || n, n);
13584             if(!m || rv){
13585                 af[n.id] = n;
13586                 n.ui.hide();
13587                 return false;
13588             }
13589             return true;
13590         };
13591         startNode.cascade(f);
13592         if(this.remove){
13593            for(var id in af){
13594                if(typeof id != "function"){
13595                    var n = af[id];
13596                    if(n && n.parentNode){
13597                        n.parentNode.removeChild(n);
13598                    }
13599                }
13600            }
13601         }
13602     },
13603
13604     /**
13605      * Clears the current filter. Note: with the "remove" option
13606      * set a filter cannot be cleared.
13607      */
13608     clear : function(){
13609         var t = this.tree;
13610         var af = this.filtered;
13611         for(var id in af){
13612             if(typeof id != "function"){
13613                 var n = af[id];
13614                 if(n){
13615                     n.ui.show();
13616                 }
13617             }
13618         }
13619         this.filtered = {};
13620     }
13621 };
13622 /*
13623  * Based on:
13624  * Ext JS Library 1.1.1
13625  * Copyright(c) 2006-2007, Ext JS, LLC.
13626  *
13627  * Originally Released Under LGPL - original licence link has changed is not relivant.
13628  *
13629  * Fork - LGPL
13630  * <script type="text/javascript">
13631  */
13632  
13633
13634 /**
13635  * @class Roo.tree.TreeSorter
13636  * Provides sorting of nodes in a TreePanel
13637  * 
13638  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13639  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13640  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13641  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13642  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13643  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13644  * @constructor
13645  * @param {TreePanel} tree
13646  * @param {Object} config
13647  */
13648 Roo.tree.TreeSorter = function(tree, config){
13649     Roo.apply(this, config);
13650     tree.on("beforechildrenrendered", this.doSort, this);
13651     tree.on("append", this.updateSort, this);
13652     tree.on("insert", this.updateSort, this);
13653     
13654     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13655     var p = this.property || "text";
13656     var sortType = this.sortType;
13657     var fs = this.folderSort;
13658     var cs = this.caseSensitive === true;
13659     var leafAttr = this.leafAttr || 'leaf';
13660
13661     this.sortFn = function(n1, n2){
13662         if(fs){
13663             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13664                 return 1;
13665             }
13666             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13667                 return -1;
13668             }
13669         }
13670         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13671         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13672         if(v1 < v2){
13673                         return dsc ? +1 : -1;
13674                 }else if(v1 > v2){
13675                         return dsc ? -1 : +1;
13676         }else{
13677                 return 0;
13678         }
13679     };
13680 };
13681
13682 Roo.tree.TreeSorter.prototype = {
13683     doSort : function(node){
13684         node.sort(this.sortFn);
13685     },
13686     
13687     compareNodes : function(n1, n2){
13688         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13689     },
13690     
13691     updateSort : function(tree, node){
13692         if(node.childrenRendered){
13693             this.doSort.defer(1, this, [node]);
13694         }
13695     }
13696 };/*
13697  * Based on:
13698  * Ext JS Library 1.1.1
13699  * Copyright(c) 2006-2007, Ext JS, LLC.
13700  *
13701  * Originally Released Under LGPL - original licence link has changed is not relivant.
13702  *
13703  * Fork - LGPL
13704  * <script type="text/javascript">
13705  */
13706
13707 if(Roo.dd.DropZone){
13708     
13709 Roo.tree.TreeDropZone = function(tree, config){
13710     this.allowParentInsert = false;
13711     this.allowContainerDrop = false;
13712     this.appendOnly = false;
13713     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13714     this.tree = tree;
13715     this.lastInsertClass = "x-tree-no-status";
13716     this.dragOverData = {};
13717 };
13718
13719 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13720     ddGroup : "TreeDD",
13721     scroll:  true,
13722     
13723     expandDelay : 1000,
13724     
13725     expandNode : function(node){
13726         if(node.hasChildNodes() && !node.isExpanded()){
13727             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13728         }
13729     },
13730     
13731     queueExpand : function(node){
13732         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13733     },
13734     
13735     cancelExpand : function(){
13736         if(this.expandProcId){
13737             clearTimeout(this.expandProcId);
13738             this.expandProcId = false;
13739         }
13740     },
13741     
13742     isValidDropPoint : function(n, pt, dd, e, data){
13743         if(!n || !data){ return false; }
13744         var targetNode = n.node;
13745         var dropNode = data.node;
13746         // default drop rules
13747         if(!(targetNode && targetNode.isTarget && pt)){
13748             return false;
13749         }
13750         if(pt == "append" && targetNode.allowChildren === false){
13751             return false;
13752         }
13753         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13754             return false;
13755         }
13756         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13757             return false;
13758         }
13759         // reuse the object
13760         var overEvent = this.dragOverData;
13761         overEvent.tree = this.tree;
13762         overEvent.target = targetNode;
13763         overEvent.data = data;
13764         overEvent.point = pt;
13765         overEvent.source = dd;
13766         overEvent.rawEvent = e;
13767         overEvent.dropNode = dropNode;
13768         overEvent.cancel = false;  
13769         var result = this.tree.fireEvent("nodedragover", overEvent);
13770         return overEvent.cancel === false && result !== false;
13771     },
13772     
13773     getDropPoint : function(e, n, dd)
13774     {
13775         var tn = n.node;
13776         if(tn.isRoot){
13777             return tn.allowChildren !== false ? "append" : false; // always append for root
13778         }
13779         var dragEl = n.ddel;
13780         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13781         var y = Roo.lib.Event.getPageY(e);
13782         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13783         
13784         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13785         var noAppend = tn.allowChildren === false;
13786         if(this.appendOnly || tn.parentNode.allowChildren === false){
13787             return noAppend ? false : "append";
13788         }
13789         var noBelow = false;
13790         if(!this.allowParentInsert){
13791             noBelow = tn.hasChildNodes() && tn.isExpanded();
13792         }
13793         var q = (b - t) / (noAppend ? 2 : 3);
13794         if(y >= t && y < (t + q)){
13795             return "above";
13796         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13797             return "below";
13798         }else{
13799             return "append";
13800         }
13801     },
13802     
13803     onNodeEnter : function(n, dd, e, data)
13804     {
13805         this.cancelExpand();
13806     },
13807     
13808     onNodeOver : function(n, dd, e, data)
13809     {
13810        
13811         var pt = this.getDropPoint(e, n, dd);
13812         var node = n.node;
13813         
13814         // auto node expand check
13815         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13816             this.queueExpand(node);
13817         }else if(pt != "append"){
13818             this.cancelExpand();
13819         }
13820         
13821         // set the insert point style on the target node
13822         var returnCls = this.dropNotAllowed;
13823         if(this.isValidDropPoint(n, pt, dd, e, data)){
13824            if(pt){
13825                var el = n.ddel;
13826                var cls;
13827                if(pt == "above"){
13828                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13829                    cls = "x-tree-drag-insert-above";
13830                }else if(pt == "below"){
13831                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13832                    cls = "x-tree-drag-insert-below";
13833                }else{
13834                    returnCls = "x-tree-drop-ok-append";
13835                    cls = "x-tree-drag-append";
13836                }
13837                if(this.lastInsertClass != cls){
13838                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13839                    this.lastInsertClass = cls;
13840                }
13841            }
13842        }
13843        return returnCls;
13844     },
13845     
13846     onNodeOut : function(n, dd, e, data){
13847         
13848         this.cancelExpand();
13849         this.removeDropIndicators(n);
13850     },
13851     
13852     onNodeDrop : function(n, dd, e, data){
13853         var point = this.getDropPoint(e, n, dd);
13854         var targetNode = n.node;
13855         targetNode.ui.startDrop();
13856         if(!this.isValidDropPoint(n, point, dd, e, data)){
13857             targetNode.ui.endDrop();
13858             return false;
13859         }
13860         // first try to find the drop node
13861         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13862         var dropEvent = {
13863             tree : this.tree,
13864             target: targetNode,
13865             data: data,
13866             point: point,
13867             source: dd,
13868             rawEvent: e,
13869             dropNode: dropNode,
13870             cancel: !dropNode   
13871         };
13872         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13873         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13874             targetNode.ui.endDrop();
13875             return false;
13876         }
13877         // allow target changing
13878         targetNode = dropEvent.target;
13879         if(point == "append" && !targetNode.isExpanded()){
13880             targetNode.expand(false, null, function(){
13881                 this.completeDrop(dropEvent);
13882             }.createDelegate(this));
13883         }else{
13884             this.completeDrop(dropEvent);
13885         }
13886         return true;
13887     },
13888     
13889     completeDrop : function(de){
13890         var ns = de.dropNode, p = de.point, t = de.target;
13891         if(!(ns instanceof Array)){
13892             ns = [ns];
13893         }
13894         var n;
13895         for(var i = 0, len = ns.length; i < len; i++){
13896             n = ns[i];
13897             if(p == "above"){
13898                 t.parentNode.insertBefore(n, t);
13899             }else if(p == "below"){
13900                 t.parentNode.insertBefore(n, t.nextSibling);
13901             }else{
13902                 t.appendChild(n);
13903             }
13904         }
13905         n.ui.focus();
13906         if(this.tree.hlDrop){
13907             n.ui.highlight();
13908         }
13909         t.ui.endDrop();
13910         this.tree.fireEvent("nodedrop", de);
13911     },
13912     
13913     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13914         if(this.tree.hlDrop){
13915             dropNode.ui.focus();
13916             dropNode.ui.highlight();
13917         }
13918         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13919     },
13920     
13921     getTree : function(){
13922         return this.tree;
13923     },
13924     
13925     removeDropIndicators : function(n){
13926         if(n && n.ddel){
13927             var el = n.ddel;
13928             Roo.fly(el).removeClass([
13929                     "x-tree-drag-insert-above",
13930                     "x-tree-drag-insert-below",
13931                     "x-tree-drag-append"]);
13932             this.lastInsertClass = "_noclass";
13933         }
13934     },
13935     
13936     beforeDragDrop : function(target, e, id){
13937         this.cancelExpand();
13938         return true;
13939     },
13940     
13941     afterRepair : function(data){
13942         if(data && Roo.enableFx){
13943             data.node.ui.highlight();
13944         }
13945         this.hideProxy();
13946     } 
13947     
13948 });
13949
13950 }
13951 /*
13952  * Based on:
13953  * Ext JS Library 1.1.1
13954  * Copyright(c) 2006-2007, Ext JS, LLC.
13955  *
13956  * Originally Released Under LGPL - original licence link has changed is not relivant.
13957  *
13958  * Fork - LGPL
13959  * <script type="text/javascript">
13960  */
13961  
13962
13963 if(Roo.dd.DragZone){
13964 Roo.tree.TreeDragZone = function(tree, config){
13965     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13966     this.tree = tree;
13967 };
13968
13969 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13970     ddGroup : "TreeDD",
13971    
13972     onBeforeDrag : function(data, e){
13973         var n = data.node;
13974         return n && n.draggable && !n.disabled;
13975     },
13976      
13977     
13978     onInitDrag : function(e){
13979         var data = this.dragData;
13980         this.tree.getSelectionModel().select(data.node);
13981         this.proxy.update("");
13982         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13983         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13984     },
13985     
13986     getRepairXY : function(e, data){
13987         return data.node.ui.getDDRepairXY();
13988     },
13989     
13990     onEndDrag : function(data, e){
13991         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13992         
13993         
13994     },
13995     
13996     onValidDrop : function(dd, e, id){
13997         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13998         this.hideProxy();
13999     },
14000     
14001     beforeInvalidDrop : function(e, id){
14002         // this scrolls the original position back into view
14003         var sm = this.tree.getSelectionModel();
14004         sm.clearSelections();
14005         sm.select(this.dragData.node);
14006     }
14007 });
14008 }/*
14009  * Based on:
14010  * Ext JS Library 1.1.1
14011  * Copyright(c) 2006-2007, Ext JS, LLC.
14012  *
14013  * Originally Released Under LGPL - original licence link has changed is not relivant.
14014  *
14015  * Fork - LGPL
14016  * <script type="text/javascript">
14017  */
14018 /**
14019  * @class Roo.tree.TreeEditor
14020  * @extends Roo.Editor
14021  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14022  * as the editor field.
14023  * @constructor
14024  * @param {Object} config (used to be the tree panel.)
14025  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14026  * 
14027  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14028  * @cfg {Roo.form.TextField} field [required] The field configuration
14029  *
14030  * 
14031  */
14032 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14033     var tree = config;
14034     var field;
14035     if (oldconfig) { // old style..
14036         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14037     } else {
14038         // new style..
14039         tree = config.tree;
14040         config.field = config.field  || {};
14041         config.field.xtype = 'TextField';
14042         field = Roo.factory(config.field, Roo.form);
14043     }
14044     config = config || {};
14045     
14046     
14047     this.addEvents({
14048         /**
14049          * @event beforenodeedit
14050          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14051          * false from the handler of this event.
14052          * @param {Editor} this
14053          * @param {Roo.tree.Node} node 
14054          */
14055         "beforenodeedit" : true
14056     });
14057     
14058     //Roo.log(config);
14059     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14060
14061     this.tree = tree;
14062
14063     tree.on('beforeclick', this.beforeNodeClick, this);
14064     tree.getTreeEl().on('mousedown', this.hide, this);
14065     this.on('complete', this.updateNode, this);
14066     this.on('beforestartedit', this.fitToTree, this);
14067     this.on('startedit', this.bindScroll, this, {delay:10});
14068     this.on('specialkey', this.onSpecialKey, this);
14069 };
14070
14071 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14072     /**
14073      * @cfg {String} alignment
14074      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14075      */
14076     alignment: "l-l",
14077     // inherit
14078     autoSize: false,
14079     /**
14080      * @cfg {Boolean} hideEl
14081      * True to hide the bound element while the editor is displayed (defaults to false)
14082      */
14083     hideEl : false,
14084     /**
14085      * @cfg {String} cls
14086      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14087      */
14088     cls: "x-small-editor x-tree-editor",
14089     /**
14090      * @cfg {Boolean} shim
14091      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14092      */
14093     shim:false,
14094     // inherit
14095     shadow:"frame",
14096     /**
14097      * @cfg {Number} maxWidth
14098      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14099      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14100      * scroll and client offsets into account prior to each edit.
14101      */
14102     maxWidth: 250,
14103
14104     editDelay : 350,
14105
14106     // private
14107     fitToTree : function(ed, el){
14108         var td = this.tree.getTreeEl().dom, nd = el.dom;
14109         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14110             td.scrollLeft = nd.offsetLeft;
14111         }
14112         var w = Math.min(
14113                 this.maxWidth,
14114                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14115         this.setSize(w, '');
14116         
14117         return this.fireEvent('beforenodeedit', this, this.editNode);
14118         
14119     },
14120
14121     // private
14122     triggerEdit : function(node){
14123         this.completeEdit();
14124         this.editNode = node;
14125         this.startEdit(node.ui.textNode, node.text);
14126     },
14127
14128     // private
14129     bindScroll : function(){
14130         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14131     },
14132
14133     // private
14134     beforeNodeClick : function(node, e){
14135         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14136         this.lastClick = new Date();
14137         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14138             e.stopEvent();
14139             this.triggerEdit(node);
14140             return false;
14141         }
14142         return true;
14143     },
14144
14145     // private
14146     updateNode : function(ed, value){
14147         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14148         this.editNode.setText(value);
14149     },
14150
14151     // private
14152     onHide : function(){
14153         Roo.tree.TreeEditor.superclass.onHide.call(this);
14154         if(this.editNode){
14155             this.editNode.ui.focus();
14156         }
14157     },
14158
14159     // private
14160     onSpecialKey : function(field, e){
14161         var k = e.getKey();
14162         if(k == e.ESC){
14163             e.stopEvent();
14164             this.cancelEdit();
14165         }else if(k == e.ENTER && !e.hasModifier()){
14166             e.stopEvent();
14167             this.completeEdit();
14168         }
14169     }
14170 });//<Script type="text/javascript">
14171 /*
14172  * Based on:
14173  * Ext JS Library 1.1.1
14174  * Copyright(c) 2006-2007, Ext JS, LLC.
14175  *
14176  * Originally Released Under LGPL - original licence link has changed is not relivant.
14177  *
14178  * Fork - LGPL
14179  * <script type="text/javascript">
14180  */
14181  
14182 /**
14183  * Not documented??? - probably should be...
14184  */
14185
14186 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14187     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14188     
14189     renderElements : function(n, a, targetNode, bulkRender){
14190         //consel.log("renderElements?");
14191         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14192
14193         var t = n.getOwnerTree();
14194         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14195         
14196         var cols = t.columns;
14197         var bw = t.borderWidth;
14198         var c = cols[0];
14199         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14200          var cb = typeof a.checked == "boolean";
14201         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14202         var colcls = 'x-t-' + tid + '-c0';
14203         var buf = [
14204             '<li class="x-tree-node">',
14205             
14206                 
14207                 '<div class="x-tree-node-el ', a.cls,'">',
14208                     // extran...
14209                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14210                 
14211                 
14212                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14213                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14214                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14215                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14216                            (a.iconCls ? ' '+a.iconCls : ''),
14217                            '" unselectable="on" />',
14218                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14219                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14220                              
14221                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14222                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14223                             '<span unselectable="on" qtip="' + tx + '">',
14224                              tx,
14225                              '</span></a>' ,
14226                     '</div>',
14227                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14228                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14229                  ];
14230         for(var i = 1, len = cols.length; i < len; i++){
14231             c = cols[i];
14232             colcls = 'x-t-' + tid + '-c' +i;
14233             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14234             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14235                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14236                       "</div>");
14237          }
14238          
14239          buf.push(
14240             '</a>',
14241             '<div class="x-clear"></div></div>',
14242             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14243             "</li>");
14244         
14245         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14246             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14247                                 n.nextSibling.ui.getEl(), buf.join(""));
14248         }else{
14249             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14250         }
14251         var el = this.wrap.firstChild;
14252         this.elRow = el;
14253         this.elNode = el.firstChild;
14254         this.ranchor = el.childNodes[1];
14255         this.ctNode = this.wrap.childNodes[1];
14256         var cs = el.firstChild.childNodes;
14257         this.indentNode = cs[0];
14258         this.ecNode = cs[1];
14259         this.iconNode = cs[2];
14260         var index = 3;
14261         if(cb){
14262             this.checkbox = cs[3];
14263             index++;
14264         }
14265         this.anchor = cs[index];
14266         
14267         this.textNode = cs[index].firstChild;
14268         
14269         //el.on("click", this.onClick, this);
14270         //el.on("dblclick", this.onDblClick, this);
14271         
14272         
14273        // console.log(this);
14274     },
14275     initEvents : function(){
14276         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14277         
14278             
14279         var a = this.ranchor;
14280
14281         var el = Roo.get(a);
14282
14283         if(Roo.isOpera){ // opera render bug ignores the CSS
14284             el.setStyle("text-decoration", "none");
14285         }
14286
14287         el.on("click", this.onClick, this);
14288         el.on("dblclick", this.onDblClick, this);
14289         el.on("contextmenu", this.onContextMenu, this);
14290         
14291     },
14292     
14293     /*onSelectedChange : function(state){
14294         if(state){
14295             this.focus();
14296             this.addClass("x-tree-selected");
14297         }else{
14298             //this.blur();
14299             this.removeClass("x-tree-selected");
14300         }
14301     },*/
14302     addClass : function(cls){
14303         if(this.elRow){
14304             Roo.fly(this.elRow).addClass(cls);
14305         }
14306         
14307     },
14308     
14309     
14310     removeClass : function(cls){
14311         if(this.elRow){
14312             Roo.fly(this.elRow).removeClass(cls);
14313         }
14314     }
14315
14316     
14317     
14318 });//<Script type="text/javascript">
14319
14320 /*
14321  * Based on:
14322  * Ext JS Library 1.1.1
14323  * Copyright(c) 2006-2007, Ext JS, LLC.
14324  *
14325  * Originally Released Under LGPL - original licence link has changed is not relivant.
14326  *
14327  * Fork - LGPL
14328  * <script type="text/javascript">
14329  */
14330  
14331
14332 /**
14333  * @class Roo.tree.ColumnTree
14334  * @extends Roo.tree.TreePanel
14335  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14336  * @cfg {int} borderWidth  compined right/left border allowance
14337  * @constructor
14338  * @param {String/HTMLElement/Element} el The container element
14339  * @param {Object} config
14340  */
14341 Roo.tree.ColumnTree =  function(el, config)
14342 {
14343    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14344    this.addEvents({
14345         /**
14346         * @event resize
14347         * Fire this event on a container when it resizes
14348         * @param {int} w Width
14349         * @param {int} h Height
14350         */
14351        "resize" : true
14352     });
14353     this.on('resize', this.onResize, this);
14354 };
14355
14356 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14357     //lines:false,
14358     
14359     
14360     borderWidth: Roo.isBorderBox ? 0 : 2, 
14361     headEls : false,
14362     
14363     render : function(){
14364         // add the header.....
14365        
14366         Roo.tree.ColumnTree.superclass.render.apply(this);
14367         
14368         this.el.addClass('x-column-tree');
14369         
14370         this.headers = this.el.createChild(
14371             {cls:'x-tree-headers'},this.innerCt.dom);
14372    
14373         var cols = this.columns, c;
14374         var totalWidth = 0;
14375         this.headEls = [];
14376         var  len = cols.length;
14377         for(var i = 0; i < len; i++){
14378              c = cols[i];
14379              totalWidth += c.width;
14380             this.headEls.push(this.headers.createChild({
14381                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14382                  cn: {
14383                      cls:'x-tree-hd-text',
14384                      html: c.header
14385                  },
14386                  style:'width:'+(c.width-this.borderWidth)+'px;'
14387              }));
14388         }
14389         this.headers.createChild({cls:'x-clear'});
14390         // prevent floats from wrapping when clipped
14391         this.headers.setWidth(totalWidth);
14392         //this.innerCt.setWidth(totalWidth);
14393         this.innerCt.setStyle({ overflow: 'auto' });
14394         this.onResize(this.width, this.height);
14395              
14396         
14397     },
14398     onResize : function(w,h)
14399     {
14400         this.height = h;
14401         this.width = w;
14402         // resize cols..
14403         this.innerCt.setWidth(this.width);
14404         this.innerCt.setHeight(this.height-20);
14405         
14406         // headers...
14407         var cols = this.columns, c;
14408         var totalWidth = 0;
14409         var expEl = false;
14410         var len = cols.length;
14411         for(var i = 0; i < len; i++){
14412             c = cols[i];
14413             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14414                 // it's the expander..
14415                 expEl  = this.headEls[i];
14416                 continue;
14417             }
14418             totalWidth += c.width;
14419             
14420         }
14421         if (expEl) {
14422             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14423         }
14424         this.headers.setWidth(w-20);
14425
14426         
14427         
14428         
14429     }
14430 });
14431 /*
14432  * Based on:
14433  * Ext JS Library 1.1.1
14434  * Copyright(c) 2006-2007, Ext JS, LLC.
14435  *
14436  * Originally Released Under LGPL - original licence link has changed is not relivant.
14437  *
14438  * Fork - LGPL
14439  * <script type="text/javascript">
14440  */
14441  
14442 /**
14443  * @class Roo.menu.Menu
14444  * @extends Roo.util.Observable
14445  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14446  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14447  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14448  * @constructor
14449  * Creates a new Menu
14450  * @param {Object} config Configuration options
14451  */
14452 Roo.menu.Menu = function(config){
14453     
14454     Roo.menu.Menu.superclass.constructor.call(this, config);
14455     
14456     this.id = this.id || Roo.id();
14457     this.addEvents({
14458         /**
14459          * @event beforeshow
14460          * Fires before this menu is displayed
14461          * @param {Roo.menu.Menu} this
14462          */
14463         beforeshow : true,
14464         /**
14465          * @event beforehide
14466          * Fires before this menu is hidden
14467          * @param {Roo.menu.Menu} this
14468          */
14469         beforehide : true,
14470         /**
14471          * @event show
14472          * Fires after this menu is displayed
14473          * @param {Roo.menu.Menu} this
14474          */
14475         show : true,
14476         /**
14477          * @event hide
14478          * Fires after this menu is hidden
14479          * @param {Roo.menu.Menu} this
14480          */
14481         hide : true,
14482         /**
14483          * @event click
14484          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14485          * @param {Roo.menu.Menu} this
14486          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14487          * @param {Roo.EventObject} e
14488          */
14489         click : true,
14490         /**
14491          * @event mouseover
14492          * Fires when the mouse is hovering over this menu
14493          * @param {Roo.menu.Menu} this
14494          * @param {Roo.EventObject} e
14495          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14496          */
14497         mouseover : true,
14498         /**
14499          * @event mouseout
14500          * Fires when the mouse exits this menu
14501          * @param {Roo.menu.Menu} this
14502          * @param {Roo.EventObject} e
14503          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14504          */
14505         mouseout : true,
14506         /**
14507          * @event itemclick
14508          * Fires when a menu item contained in this menu is clicked
14509          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14510          * @param {Roo.EventObject} e
14511          */
14512         itemclick: true
14513     });
14514     if (this.registerMenu) {
14515         Roo.menu.MenuMgr.register(this);
14516     }
14517     
14518     var mis = this.items;
14519     this.items = new Roo.util.MixedCollection();
14520     if(mis){
14521         this.add.apply(this, mis);
14522     }
14523 };
14524
14525 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14526     /**
14527      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14528      */
14529     minWidth : 120,
14530     /**
14531      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14532      * for bottom-right shadow (defaults to "sides")
14533      */
14534     shadow : "sides",
14535     /**
14536      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14537      * this menu (defaults to "tl-tr?")
14538      */
14539     subMenuAlign : "tl-tr?",
14540     /**
14541      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14542      * relative to its element of origin (defaults to "tl-bl?")
14543      */
14544     defaultAlign : "tl-bl?",
14545     /**
14546      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14547      */
14548     allowOtherMenus : false,
14549     /**
14550      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14551      */
14552     registerMenu : true,
14553
14554     hidden:true,
14555
14556     // private
14557     render : function(){
14558         if(this.el){
14559             return;
14560         }
14561         var el = this.el = new Roo.Layer({
14562             cls: "x-menu",
14563             shadow:this.shadow,
14564             constrain: false,
14565             parentEl: this.parentEl || document.body,
14566             zindex:15000
14567         });
14568
14569         this.keyNav = new Roo.menu.MenuNav(this);
14570
14571         if(this.plain){
14572             el.addClass("x-menu-plain");
14573         }
14574         if(this.cls){
14575             el.addClass(this.cls);
14576         }
14577         // generic focus element
14578         this.focusEl = el.createChild({
14579             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14580         });
14581         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14582         //disabling touch- as it's causing issues ..
14583         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14584         ul.on('click'   , this.onClick, this);
14585         
14586         
14587         ul.on("mouseover", this.onMouseOver, this);
14588         ul.on("mouseout", this.onMouseOut, this);
14589         this.items.each(function(item){
14590             if (item.hidden) {
14591                 return;
14592             }
14593             
14594             var li = document.createElement("li");
14595             li.className = "x-menu-list-item";
14596             ul.dom.appendChild(li);
14597             item.render(li, this);
14598         }, this);
14599         this.ul = ul;
14600         this.autoWidth();
14601     },
14602
14603     // private
14604     autoWidth : function(){
14605         var el = this.el, ul = this.ul;
14606         if(!el){
14607             return;
14608         }
14609         var w = this.width;
14610         if(w){
14611             el.setWidth(w);
14612         }else if(Roo.isIE){
14613             el.setWidth(this.minWidth);
14614             var t = el.dom.offsetWidth; // force recalc
14615             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14616         }
14617     },
14618
14619     // private
14620     delayAutoWidth : function(){
14621         if(this.rendered){
14622             if(!this.awTask){
14623                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14624             }
14625             this.awTask.delay(20);
14626         }
14627     },
14628
14629     // private
14630     findTargetItem : function(e){
14631         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14632         if(t && t.menuItemId){
14633             return this.items.get(t.menuItemId);
14634         }
14635     },
14636
14637     // private
14638     onClick : function(e){
14639         Roo.log("menu.onClick");
14640         var t = this.findTargetItem(e);
14641         if(!t){
14642             return;
14643         }
14644         Roo.log(e);
14645         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14646             if(t == this.activeItem && t.shouldDeactivate(e)){
14647                 this.activeItem.deactivate();
14648                 delete this.activeItem;
14649                 return;
14650             }
14651             if(t.canActivate){
14652                 this.setActiveItem(t, true);
14653             }
14654             return;
14655             
14656             
14657         }
14658         
14659         t.onClick(e);
14660         this.fireEvent("click", this, t, e);
14661     },
14662
14663     // private
14664     setActiveItem : function(item, autoExpand){
14665         if(item != this.activeItem){
14666             if(this.activeItem){
14667                 this.activeItem.deactivate();
14668             }
14669             this.activeItem = item;
14670             item.activate(autoExpand);
14671         }else if(autoExpand){
14672             item.expandMenu();
14673         }
14674     },
14675
14676     // private
14677     tryActivate : function(start, step){
14678         var items = this.items;
14679         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14680             var item = items.get(i);
14681             if(!item.disabled && item.canActivate){
14682                 this.setActiveItem(item, false);
14683                 return item;
14684             }
14685         }
14686         return false;
14687     },
14688
14689     // private
14690     onMouseOver : function(e){
14691         var t;
14692         if(t = this.findTargetItem(e)){
14693             if(t.canActivate && !t.disabled){
14694                 this.setActiveItem(t, true);
14695             }
14696         }
14697         this.fireEvent("mouseover", this, e, t);
14698     },
14699
14700     // private
14701     onMouseOut : function(e){
14702         var t;
14703         if(t = this.findTargetItem(e)){
14704             if(t == this.activeItem && t.shouldDeactivate(e)){
14705                 this.activeItem.deactivate();
14706                 delete this.activeItem;
14707             }
14708         }
14709         this.fireEvent("mouseout", this, e, t);
14710     },
14711
14712     /**
14713      * Read-only.  Returns true if the menu is currently displayed, else false.
14714      * @type Boolean
14715      */
14716     isVisible : function(){
14717         return this.el && !this.hidden;
14718     },
14719
14720     /**
14721      * Displays this menu relative to another element
14722      * @param {String/HTMLElement/Roo.Element} element The element to align to
14723      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14724      * the element (defaults to this.defaultAlign)
14725      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14726      */
14727     show : function(el, pos, parentMenu){
14728         this.parentMenu = parentMenu;
14729         if(!this.el){
14730             this.render();
14731         }
14732         this.fireEvent("beforeshow", this);
14733         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14734     },
14735
14736     /**
14737      * Displays this menu at a specific xy position
14738      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14739      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14740      */
14741     showAt : function(xy, parentMenu, /* private: */_e){
14742         this.parentMenu = parentMenu;
14743         if(!this.el){
14744             this.render();
14745         }
14746         if(_e !== false){
14747             this.fireEvent("beforeshow", this);
14748             xy = this.el.adjustForConstraints(xy);
14749         }
14750         this.el.setXY(xy);
14751         this.el.show();
14752         this.hidden = false;
14753         this.focus();
14754         this.fireEvent("show", this);
14755     },
14756
14757     focus : function(){
14758         if(!this.hidden){
14759             this.doFocus.defer(50, this);
14760         }
14761     },
14762
14763     doFocus : function(){
14764         if(!this.hidden){
14765             this.focusEl.focus();
14766         }
14767     },
14768
14769     /**
14770      * Hides this menu and optionally all parent menus
14771      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14772      */
14773     hide : function(deep){
14774         if(this.el && this.isVisible()){
14775             this.fireEvent("beforehide", this);
14776             if(this.activeItem){
14777                 this.activeItem.deactivate();
14778                 this.activeItem = null;
14779             }
14780             this.el.hide();
14781             this.hidden = true;
14782             this.fireEvent("hide", this);
14783         }
14784         if(deep === true && this.parentMenu){
14785             this.parentMenu.hide(true);
14786         }
14787     },
14788
14789     /**
14790      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14791      * Any of the following are valid:
14792      * <ul>
14793      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14794      * <li>An HTMLElement object which will be converted to a menu item</li>
14795      * <li>A menu item config object that will be created as a new menu item</li>
14796      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14797      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14798      * </ul>
14799      * Usage:
14800      * <pre><code>
14801 // Create the menu
14802 var menu = new Roo.menu.Menu();
14803
14804 // Create a menu item to add by reference
14805 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14806
14807 // Add a bunch of items at once using different methods.
14808 // Only the last item added will be returned.
14809 var item = menu.add(
14810     menuItem,                // add existing item by ref
14811     'Dynamic Item',          // new TextItem
14812     '-',                     // new separator
14813     { text: 'Config Item' }  // new item by config
14814 );
14815 </code></pre>
14816      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14817      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14818      */
14819     add : function(){
14820         var a = arguments, l = a.length, item;
14821         for(var i = 0; i < l; i++){
14822             var el = a[i];
14823             if ((typeof(el) == "object") && el.xtype && el.xns) {
14824                 el = Roo.factory(el, Roo.menu);
14825             }
14826             
14827             if(el.render){ // some kind of Item
14828                 item = this.addItem(el);
14829             }else if(typeof el == "string"){ // string
14830                 if(el == "separator" || el == "-"){
14831                     item = this.addSeparator();
14832                 }else{
14833                     item = this.addText(el);
14834                 }
14835             }else if(el.tagName || el.el){ // element
14836                 item = this.addElement(el);
14837             }else if(typeof el == "object"){ // must be menu item config?
14838                 item = this.addMenuItem(el);
14839             }
14840         }
14841         return item;
14842     },
14843
14844     /**
14845      * Returns this menu's underlying {@link Roo.Element} object
14846      * @return {Roo.Element} The element
14847      */
14848     getEl : function(){
14849         if(!this.el){
14850             this.render();
14851         }
14852         return this.el;
14853     },
14854
14855     /**
14856      * Adds a separator bar to the menu
14857      * @return {Roo.menu.Item} The menu item that was added
14858      */
14859     addSeparator : function(){
14860         return this.addItem(new Roo.menu.Separator());
14861     },
14862
14863     /**
14864      * Adds an {@link Roo.Element} object to the menu
14865      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14866      * @return {Roo.menu.Item} The menu item that was added
14867      */
14868     addElement : function(el){
14869         return this.addItem(new Roo.menu.BaseItem(el));
14870     },
14871
14872     /**
14873      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14874      * @param {Roo.menu.Item} item The menu item to add
14875      * @return {Roo.menu.Item} The menu item that was added
14876      */
14877     addItem : function(item){
14878         this.items.add(item);
14879         if(this.ul){
14880             var li = document.createElement("li");
14881             li.className = "x-menu-list-item";
14882             this.ul.dom.appendChild(li);
14883             item.render(li, this);
14884             this.delayAutoWidth();
14885         }
14886         return item;
14887     },
14888
14889     /**
14890      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14891      * @param {Object} config A MenuItem config object
14892      * @return {Roo.menu.Item} The menu item that was added
14893      */
14894     addMenuItem : function(config){
14895         if(!(config instanceof Roo.menu.Item)){
14896             if(typeof config.checked == "boolean"){ // must be check menu item config?
14897                 config = new Roo.menu.CheckItem(config);
14898             }else{
14899                 config = new Roo.menu.Item(config);
14900             }
14901         }
14902         return this.addItem(config);
14903     },
14904
14905     /**
14906      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14907      * @param {String} text The text to display in the menu item
14908      * @return {Roo.menu.Item} The menu item that was added
14909      */
14910     addText : function(text){
14911         return this.addItem(new Roo.menu.TextItem({ text : text }));
14912     },
14913
14914     /**
14915      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14916      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14917      * @param {Roo.menu.Item} item The menu item to add
14918      * @return {Roo.menu.Item} The menu item that was added
14919      */
14920     insert : function(index, item){
14921         this.items.insert(index, item);
14922         if(this.ul){
14923             var li = document.createElement("li");
14924             li.className = "x-menu-list-item";
14925             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14926             item.render(li, this);
14927             this.delayAutoWidth();
14928         }
14929         return item;
14930     },
14931
14932     /**
14933      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14934      * @param {Roo.menu.Item} item The menu item to remove
14935      */
14936     remove : function(item){
14937         this.items.removeKey(item.id);
14938         item.destroy();
14939     },
14940
14941     /**
14942      * Removes and destroys all items in the menu
14943      */
14944     removeAll : function(){
14945         var f;
14946         while(f = this.items.first()){
14947             this.remove(f);
14948         }
14949     }
14950 });
14951
14952 // MenuNav is a private utility class used internally by the Menu
14953 Roo.menu.MenuNav = function(menu){
14954     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14955     this.scope = this.menu = menu;
14956 };
14957
14958 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14959     doRelay : function(e, h){
14960         var k = e.getKey();
14961         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14962             this.menu.tryActivate(0, 1);
14963             return false;
14964         }
14965         return h.call(this.scope || this, e, this.menu);
14966     },
14967
14968     up : function(e, m){
14969         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14970             m.tryActivate(m.items.length-1, -1);
14971         }
14972     },
14973
14974     down : function(e, m){
14975         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14976             m.tryActivate(0, 1);
14977         }
14978     },
14979
14980     right : function(e, m){
14981         if(m.activeItem){
14982             m.activeItem.expandMenu(true);
14983         }
14984     },
14985
14986     left : function(e, m){
14987         m.hide();
14988         if(m.parentMenu && m.parentMenu.activeItem){
14989             m.parentMenu.activeItem.activate();
14990         }
14991     },
14992
14993     enter : function(e, m){
14994         if(m.activeItem){
14995             e.stopPropagation();
14996             m.activeItem.onClick(e);
14997             m.fireEvent("click", this, m.activeItem);
14998             return true;
14999         }
15000     }
15001 });/*
15002  * Based on:
15003  * Ext JS Library 1.1.1
15004  * Copyright(c) 2006-2007, Ext JS, LLC.
15005  *
15006  * Originally Released Under LGPL - original licence link has changed is not relivant.
15007  *
15008  * Fork - LGPL
15009  * <script type="text/javascript">
15010  */
15011  
15012 /**
15013  * @class Roo.menu.MenuMgr
15014  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15015  * @static
15016  */
15017 Roo.menu.MenuMgr = function(){
15018    var menus, active, groups = {}, attached = false, lastShow = new Date();
15019
15020    // private - called when first menu is created
15021    function init(){
15022        menus = {};
15023        active = new Roo.util.MixedCollection();
15024        Roo.get(document).addKeyListener(27, function(){
15025            if(active.length > 0){
15026                hideAll();
15027            }
15028        });
15029    }
15030
15031    // private
15032    function hideAll(){
15033        if(active && active.length > 0){
15034            var c = active.clone();
15035            c.each(function(m){
15036                m.hide();
15037            });
15038        }
15039    }
15040
15041    // private
15042    function onHide(m){
15043        active.remove(m);
15044        if(active.length < 1){
15045            Roo.get(document).un("mousedown", onMouseDown);
15046            attached = false;
15047        }
15048    }
15049
15050    // private
15051    function onShow(m){
15052        var last = active.last();
15053        lastShow = new Date();
15054        active.add(m);
15055        if(!attached){
15056            Roo.get(document).on("mousedown", onMouseDown);
15057            attached = true;
15058        }
15059        if(m.parentMenu){
15060           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15061           m.parentMenu.activeChild = m;
15062        }else if(last && last.isVisible()){
15063           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15064        }
15065    }
15066
15067    // private
15068    function onBeforeHide(m){
15069        if(m.activeChild){
15070            m.activeChild.hide();
15071        }
15072        if(m.autoHideTimer){
15073            clearTimeout(m.autoHideTimer);
15074            delete m.autoHideTimer;
15075        }
15076    }
15077
15078    // private
15079    function onBeforeShow(m){
15080        var pm = m.parentMenu;
15081        if(!pm && !m.allowOtherMenus){
15082            hideAll();
15083        }else if(pm && pm.activeChild && active != m){
15084            pm.activeChild.hide();
15085        }
15086    }
15087
15088    // private
15089    function onMouseDown(e){
15090        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15091            hideAll();
15092        }
15093    }
15094
15095    // private
15096    function onBeforeCheck(mi, state){
15097        if(state){
15098            var g = groups[mi.group];
15099            for(var i = 0, l = g.length; i < l; i++){
15100                if(g[i] != mi){
15101                    g[i].setChecked(false);
15102                }
15103            }
15104        }
15105    }
15106
15107    return {
15108
15109        /**
15110         * Hides all menus that are currently visible
15111         */
15112        hideAll : function(){
15113             hideAll();  
15114        },
15115
15116        // private
15117        register : function(menu){
15118            if(!menus){
15119                init();
15120            }
15121            menus[menu.id] = menu;
15122            menu.on("beforehide", onBeforeHide);
15123            menu.on("hide", onHide);
15124            menu.on("beforeshow", onBeforeShow);
15125            menu.on("show", onShow);
15126            var g = menu.group;
15127            if(g && menu.events["checkchange"]){
15128                if(!groups[g]){
15129                    groups[g] = [];
15130                }
15131                groups[g].push(menu);
15132                menu.on("checkchange", onCheck);
15133            }
15134        },
15135
15136         /**
15137          * Returns a {@link Roo.menu.Menu} object
15138          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15139          * be used to generate and return a new Menu instance.
15140          */
15141        get : function(menu){
15142            if(typeof menu == "string"){ // menu id
15143                return menus[menu];
15144            }else if(menu.events){  // menu instance
15145                return menu;
15146            }else if(typeof menu.length == 'number'){ // array of menu items?
15147                return new Roo.menu.Menu({items:menu});
15148            }else{ // otherwise, must be a config
15149                return new Roo.menu.Menu(menu);
15150            }
15151        },
15152
15153        // private
15154        unregister : function(menu){
15155            delete menus[menu.id];
15156            menu.un("beforehide", onBeforeHide);
15157            menu.un("hide", onHide);
15158            menu.un("beforeshow", onBeforeShow);
15159            menu.un("show", onShow);
15160            var g = menu.group;
15161            if(g && menu.events["checkchange"]){
15162                groups[g].remove(menu);
15163                menu.un("checkchange", onCheck);
15164            }
15165        },
15166
15167        // private
15168        registerCheckable : function(menuItem){
15169            var g = menuItem.group;
15170            if(g){
15171                if(!groups[g]){
15172                    groups[g] = [];
15173                }
15174                groups[g].push(menuItem);
15175                menuItem.on("beforecheckchange", onBeforeCheck);
15176            }
15177        },
15178
15179        // private
15180        unregisterCheckable : function(menuItem){
15181            var g = menuItem.group;
15182            if(g){
15183                groups[g].remove(menuItem);
15184                menuItem.un("beforecheckchange", onBeforeCheck);
15185            }
15186        }
15187    };
15188 }();/*
15189  * Based on:
15190  * Ext JS Library 1.1.1
15191  * Copyright(c) 2006-2007, Ext JS, LLC.
15192  *
15193  * Originally Released Under LGPL - original licence link has changed is not relivant.
15194  *
15195  * Fork - LGPL
15196  * <script type="text/javascript">
15197  */
15198  
15199
15200 /**
15201  * @class Roo.menu.BaseItem
15202  * @extends Roo.Component
15203  * @abstract
15204  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15205  * management and base configuration options shared by all menu components.
15206  * @constructor
15207  * Creates a new BaseItem
15208  * @param {Object} config Configuration options
15209  */
15210 Roo.menu.BaseItem = function(config){
15211     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15212
15213     this.addEvents({
15214         /**
15215          * @event click
15216          * Fires when this item is clicked
15217          * @param {Roo.menu.BaseItem} this
15218          * @param {Roo.EventObject} e
15219          */
15220         click: true,
15221         /**
15222          * @event activate
15223          * Fires when this item is activated
15224          * @param {Roo.menu.BaseItem} this
15225          */
15226         activate : true,
15227         /**
15228          * @event deactivate
15229          * Fires when this item is deactivated
15230          * @param {Roo.menu.BaseItem} this
15231          */
15232         deactivate : true
15233     });
15234
15235     if(this.handler){
15236         this.on("click", this.handler, this.scope, true);
15237     }
15238 };
15239
15240 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15241     /**
15242      * @cfg {Function} handler
15243      * A function that will handle the click event of this menu item (defaults to undefined)
15244      */
15245     /**
15246      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15247      */
15248     canActivate : false,
15249     
15250      /**
15251      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15252      */
15253     hidden: false,
15254     
15255     /**
15256      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15257      */
15258     activeClass : "x-menu-item-active",
15259     /**
15260      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15261      */
15262     hideOnClick : true,
15263     /**
15264      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15265      */
15266     hideDelay : 100,
15267
15268     // private
15269     ctype: "Roo.menu.BaseItem",
15270
15271     // private
15272     actionMode : "container",
15273
15274     // private
15275     render : function(container, parentMenu){
15276         this.parentMenu = parentMenu;
15277         Roo.menu.BaseItem.superclass.render.call(this, container);
15278         this.container.menuItemId = this.id;
15279     },
15280
15281     // private
15282     onRender : function(container, position){
15283         this.el = Roo.get(this.el);
15284         container.dom.appendChild(this.el.dom);
15285     },
15286
15287     // private
15288     onClick : function(e){
15289         if(!this.disabled && this.fireEvent("click", this, e) !== false
15290                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15291             this.handleClick(e);
15292         }else{
15293             e.stopEvent();
15294         }
15295     },
15296
15297     // private
15298     activate : function(){
15299         if(this.disabled){
15300             return false;
15301         }
15302         var li = this.container;
15303         li.addClass(this.activeClass);
15304         this.region = li.getRegion().adjust(2, 2, -2, -2);
15305         this.fireEvent("activate", this);
15306         return true;
15307     },
15308
15309     // private
15310     deactivate : function(){
15311         this.container.removeClass(this.activeClass);
15312         this.fireEvent("deactivate", this);
15313     },
15314
15315     // private
15316     shouldDeactivate : function(e){
15317         return !this.region || !this.region.contains(e.getPoint());
15318     },
15319
15320     // private
15321     handleClick : function(e){
15322         if(this.hideOnClick){
15323             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15324         }
15325     },
15326
15327     // private
15328     expandMenu : function(autoActivate){
15329         // do nothing
15330     },
15331
15332     // private
15333     hideMenu : function(){
15334         // do nothing
15335     }
15336 });/*
15337  * Based on:
15338  * Ext JS Library 1.1.1
15339  * Copyright(c) 2006-2007, Ext JS, LLC.
15340  *
15341  * Originally Released Under LGPL - original licence link has changed is not relivant.
15342  *
15343  * Fork - LGPL
15344  * <script type="text/javascript">
15345  */
15346  
15347 /**
15348  * @class Roo.menu.Adapter
15349  * @extends Roo.menu.BaseItem
15350  * @abstract
15351  * 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.
15352  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15353  * @constructor
15354  * Creates a new Adapter
15355  * @param {Object} config Configuration options
15356  */
15357 Roo.menu.Adapter = function(component, config){
15358     Roo.menu.Adapter.superclass.constructor.call(this, config);
15359     this.component = component;
15360 };
15361 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15362     // private
15363     canActivate : true,
15364
15365     // private
15366     onRender : function(container, position){
15367         this.component.render(container);
15368         this.el = this.component.getEl();
15369     },
15370
15371     // private
15372     activate : function(){
15373         if(this.disabled){
15374             return false;
15375         }
15376         this.component.focus();
15377         this.fireEvent("activate", this);
15378         return true;
15379     },
15380
15381     // private
15382     deactivate : function(){
15383         this.fireEvent("deactivate", this);
15384     },
15385
15386     // private
15387     disable : function(){
15388         this.component.disable();
15389         Roo.menu.Adapter.superclass.disable.call(this);
15390     },
15391
15392     // private
15393     enable : function(){
15394         this.component.enable();
15395         Roo.menu.Adapter.superclass.enable.call(this);
15396     }
15397 });/*
15398  * Based on:
15399  * Ext JS Library 1.1.1
15400  * Copyright(c) 2006-2007, Ext JS, LLC.
15401  *
15402  * Originally Released Under LGPL - original licence link has changed is not relivant.
15403  *
15404  * Fork - LGPL
15405  * <script type="text/javascript">
15406  */
15407
15408 /**
15409  * @class Roo.menu.TextItem
15410  * @extends Roo.menu.BaseItem
15411  * Adds a static text string to a menu, usually used as either a heading or group separator.
15412  * Note: old style constructor with text is still supported.
15413  * 
15414  * @constructor
15415  * Creates a new TextItem
15416  * @param {Object} cfg Configuration
15417  */
15418 Roo.menu.TextItem = function(cfg){
15419     if (typeof(cfg) == 'string') {
15420         this.text = cfg;
15421     } else {
15422         Roo.apply(this,cfg);
15423     }
15424     
15425     Roo.menu.TextItem.superclass.constructor.call(this);
15426 };
15427
15428 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15429     /**
15430      * @cfg {String} text Text to show on item.
15431      */
15432     text : '',
15433     
15434     /**
15435      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15436      */
15437     hideOnClick : false,
15438     /**
15439      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15440      */
15441     itemCls : "x-menu-text",
15442
15443     // private
15444     onRender : function(){
15445         var s = document.createElement("span");
15446         s.className = this.itemCls;
15447         s.innerHTML = this.text;
15448         this.el = s;
15449         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15450     }
15451 });/*
15452  * Based on:
15453  * Ext JS Library 1.1.1
15454  * Copyright(c) 2006-2007, Ext JS, LLC.
15455  *
15456  * Originally Released Under LGPL - original licence link has changed is not relivant.
15457  *
15458  * Fork - LGPL
15459  * <script type="text/javascript">
15460  */
15461
15462 /**
15463  * @class Roo.menu.Separator
15464  * @extends Roo.menu.BaseItem
15465  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15466  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15467  * @constructor
15468  * @param {Object} config Configuration options
15469  */
15470 Roo.menu.Separator = function(config){
15471     Roo.menu.Separator.superclass.constructor.call(this, config);
15472 };
15473
15474 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15475     /**
15476      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15477      */
15478     itemCls : "x-menu-sep",
15479     /**
15480      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15481      */
15482     hideOnClick : false,
15483
15484     // private
15485     onRender : function(li){
15486         var s = document.createElement("span");
15487         s.className = this.itemCls;
15488         s.innerHTML = "&#160;";
15489         this.el = s;
15490         li.addClass("x-menu-sep-li");
15491         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15492     }
15493 });/*
15494  * Based on:
15495  * Ext JS Library 1.1.1
15496  * Copyright(c) 2006-2007, Ext JS, LLC.
15497  *
15498  * Originally Released Under LGPL - original licence link has changed is not relivant.
15499  *
15500  * Fork - LGPL
15501  * <script type="text/javascript">
15502  */
15503 /**
15504  * @class Roo.menu.Item
15505  * @extends Roo.menu.BaseItem
15506  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15507  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15508  * activation and click handling.
15509  * @constructor
15510  * Creates a new Item
15511  * @param {Object} config Configuration options
15512  */
15513 Roo.menu.Item = function(config){
15514     Roo.menu.Item.superclass.constructor.call(this, config);
15515     if(this.menu){
15516         this.menu = Roo.menu.MenuMgr.get(this.menu);
15517     }
15518 };
15519 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15520     /**
15521      * @cfg {Roo.menu.Menu} menu
15522      * A Sub menu
15523      */
15524     /**
15525      * @cfg {String} text
15526      * The text to show on the menu item.
15527      */
15528     text: '',
15529      /**
15530      * @cfg {String} html to render in menu
15531      * The text to show on the menu item (HTML version).
15532      */
15533     html: '',
15534     /**
15535      * @cfg {String} icon
15536      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15537      */
15538     icon: undefined,
15539     /**
15540      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15541      */
15542     itemCls : "x-menu-item",
15543     /**
15544      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15545      */
15546     canActivate : true,
15547     /**
15548      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15549      */
15550     showDelay: 200,
15551     // doc'd in BaseItem
15552     hideDelay: 200,
15553
15554     // private
15555     ctype: "Roo.menu.Item",
15556     
15557     // private
15558     onRender : function(container, position){
15559         var el = document.createElement("a");
15560         el.hideFocus = true;
15561         el.unselectable = "on";
15562         el.href = this.href || "#";
15563         if(this.hrefTarget){
15564             el.target = this.hrefTarget;
15565         }
15566         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15567         
15568         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15569         
15570         el.innerHTML = String.format(
15571                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15572                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15573         this.el = el;
15574         Roo.menu.Item.superclass.onRender.call(this, container, position);
15575     },
15576
15577     /**
15578      * Sets the text to display in this menu item
15579      * @param {String} text The text to display
15580      * @param {Boolean} isHTML true to indicate text is pure html.
15581      */
15582     setText : function(text, isHTML){
15583         if (isHTML) {
15584             this.html = text;
15585         } else {
15586             this.text = text;
15587             this.html = '';
15588         }
15589         if(this.rendered){
15590             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15591      
15592             this.el.update(String.format(
15593                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15594                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15595             this.parentMenu.autoWidth();
15596         }
15597     },
15598
15599     // private
15600     handleClick : function(e){
15601         if(!this.href){ // if no link defined, stop the event automatically
15602             e.stopEvent();
15603         }
15604         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15605     },
15606
15607     // private
15608     activate : function(autoExpand){
15609         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15610             this.focus();
15611             if(autoExpand){
15612                 this.expandMenu();
15613             }
15614         }
15615         return true;
15616     },
15617
15618     // private
15619     shouldDeactivate : function(e){
15620         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15621             if(this.menu && this.menu.isVisible()){
15622                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15623             }
15624             return true;
15625         }
15626         return false;
15627     },
15628
15629     // private
15630     deactivate : function(){
15631         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15632         this.hideMenu();
15633     },
15634
15635     // private
15636     expandMenu : function(autoActivate){
15637         if(!this.disabled && this.menu){
15638             clearTimeout(this.hideTimer);
15639             delete this.hideTimer;
15640             if(!this.menu.isVisible() && !this.showTimer){
15641                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15642             }else if (this.menu.isVisible() && autoActivate){
15643                 this.menu.tryActivate(0, 1);
15644             }
15645         }
15646     },
15647
15648     // private
15649     deferExpand : function(autoActivate){
15650         delete this.showTimer;
15651         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15652         if(autoActivate){
15653             this.menu.tryActivate(0, 1);
15654         }
15655     },
15656
15657     // private
15658     hideMenu : function(){
15659         clearTimeout(this.showTimer);
15660         delete this.showTimer;
15661         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15662             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15663         }
15664     },
15665
15666     // private
15667     deferHide : function(){
15668         delete this.hideTimer;
15669         this.menu.hide();
15670     }
15671 });/*
15672  * Based on:
15673  * Ext JS Library 1.1.1
15674  * Copyright(c) 2006-2007, Ext JS, LLC.
15675  *
15676  * Originally Released Under LGPL - original licence link has changed is not relivant.
15677  *
15678  * Fork - LGPL
15679  * <script type="text/javascript">
15680  */
15681  
15682 /**
15683  * @class Roo.menu.CheckItem
15684  * @extends Roo.menu.Item
15685  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15686  * @constructor
15687  * Creates a new CheckItem
15688  * @param {Object} config Configuration options
15689  */
15690 Roo.menu.CheckItem = function(config){
15691     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15692     this.addEvents({
15693         /**
15694          * @event beforecheckchange
15695          * Fires before the checked value is set, providing an opportunity to cancel if needed
15696          * @param {Roo.menu.CheckItem} this
15697          * @param {Boolean} checked The new checked value that will be set
15698          */
15699         "beforecheckchange" : true,
15700         /**
15701          * @event checkchange
15702          * Fires after the checked value has been set
15703          * @param {Roo.menu.CheckItem} this
15704          * @param {Boolean} checked The checked value that was set
15705          */
15706         "checkchange" : true
15707     });
15708     if(this.checkHandler){
15709         this.on('checkchange', this.checkHandler, this.scope);
15710     }
15711 };
15712 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15713     /**
15714      * @cfg {String} group
15715      * All check items with the same group name will automatically be grouped into a single-select
15716      * radio button group (defaults to '')
15717      */
15718     /**
15719      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15720      */
15721     itemCls : "x-menu-item x-menu-check-item",
15722     /**
15723      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15724      */
15725     groupClass : "x-menu-group-item",
15726
15727     /**
15728      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15729      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15730      * initialized with checked = true will be rendered as checked.
15731      */
15732     checked: false,
15733
15734     // private
15735     ctype: "Roo.menu.CheckItem",
15736
15737     // private
15738     onRender : function(c){
15739         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15740         if(this.group){
15741             this.el.addClass(this.groupClass);
15742         }
15743         Roo.menu.MenuMgr.registerCheckable(this);
15744         if(this.checked){
15745             this.checked = false;
15746             this.setChecked(true, true);
15747         }
15748     },
15749
15750     // private
15751     destroy : function(){
15752         if(this.rendered){
15753             Roo.menu.MenuMgr.unregisterCheckable(this);
15754         }
15755         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15756     },
15757
15758     /**
15759      * Set the checked state of this item
15760      * @param {Boolean} checked The new checked value
15761      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15762      */
15763     setChecked : function(state, suppressEvent){
15764         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15765             if(this.container){
15766                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15767             }
15768             this.checked = state;
15769             if(suppressEvent !== true){
15770                 this.fireEvent("checkchange", this, state);
15771             }
15772         }
15773     },
15774
15775     // private
15776     handleClick : function(e){
15777        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15778            this.setChecked(!this.checked);
15779        }
15780        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15781     }
15782 });/*
15783  * Based on:
15784  * Ext JS Library 1.1.1
15785  * Copyright(c) 2006-2007, Ext JS, LLC.
15786  *
15787  * Originally Released Under LGPL - original licence link has changed is not relivant.
15788  *
15789  * Fork - LGPL
15790  * <script type="text/javascript">
15791  */
15792  
15793 /**
15794  * @class Roo.menu.DateItem
15795  * @extends Roo.menu.Adapter
15796  * A menu item that wraps the {@link Roo.DatPicker} component.
15797  * @constructor
15798  * Creates a new DateItem
15799  * @param {Object} config Configuration options
15800  */
15801 Roo.menu.DateItem = function(config){
15802     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15803     /** The Roo.DatePicker object @type Roo.DatePicker */
15804     this.picker = this.component;
15805     this.addEvents({select: true});
15806     
15807     this.picker.on("render", function(picker){
15808         picker.getEl().swallowEvent("click");
15809         picker.container.addClass("x-menu-date-item");
15810     });
15811
15812     this.picker.on("select", this.onSelect, this);
15813 };
15814
15815 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15816     // private
15817     onSelect : function(picker, date){
15818         this.fireEvent("select", this, date, picker);
15819         Roo.menu.DateItem.superclass.handleClick.call(this);
15820     }
15821 });/*
15822  * Based on:
15823  * Ext JS Library 1.1.1
15824  * Copyright(c) 2006-2007, Ext JS, LLC.
15825  *
15826  * Originally Released Under LGPL - original licence link has changed is not relivant.
15827  *
15828  * Fork - LGPL
15829  * <script type="text/javascript">
15830  */
15831  
15832 /**
15833  * @class Roo.menu.ColorItem
15834  * @extends Roo.menu.Adapter
15835  * A menu item that wraps the {@link Roo.ColorPalette} component.
15836  * @constructor
15837  * Creates a new ColorItem
15838  * @param {Object} config Configuration options
15839  */
15840 Roo.menu.ColorItem = function(config){
15841     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15842     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15843     this.palette = this.component;
15844     this.relayEvents(this.palette, ["select"]);
15845     if(this.selectHandler){
15846         this.on('select', this.selectHandler, this.scope);
15847     }
15848 };
15849 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15850  * Based on:
15851  * Ext JS Library 1.1.1
15852  * Copyright(c) 2006-2007, Ext JS, LLC.
15853  *
15854  * Originally Released Under LGPL - original licence link has changed is not relivant.
15855  *
15856  * Fork - LGPL
15857  * <script type="text/javascript">
15858  */
15859  
15860
15861 /**
15862  * @class Roo.menu.DateMenu
15863  * @extends Roo.menu.Menu
15864  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15865  * @constructor
15866  * Creates a new DateMenu
15867  * @param {Object} config Configuration options
15868  */
15869 Roo.menu.DateMenu = function(config){
15870     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15871     this.plain = true;
15872     var di = new Roo.menu.DateItem(config);
15873     this.add(di);
15874     /**
15875      * The {@link Roo.DatePicker} instance for this DateMenu
15876      * @type DatePicker
15877      */
15878     this.picker = di.picker;
15879     /**
15880      * @event select
15881      * @param {DatePicker} picker
15882      * @param {Date} date
15883      */
15884     this.relayEvents(di, ["select"]);
15885     this.on('beforeshow', function(){
15886         if(this.picker){
15887             this.picker.hideMonthPicker(false);
15888         }
15889     }, this);
15890 };
15891 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15892     cls:'x-date-menu'
15893 });/*
15894  * Based on:
15895  * Ext JS Library 1.1.1
15896  * Copyright(c) 2006-2007, Ext JS, LLC.
15897  *
15898  * Originally Released Under LGPL - original licence link has changed is not relivant.
15899  *
15900  * Fork - LGPL
15901  * <script type="text/javascript">
15902  */
15903  
15904
15905 /**
15906  * @class Roo.menu.ColorMenu
15907  * @extends Roo.menu.Menu
15908  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15909  * @constructor
15910  * Creates a new ColorMenu
15911  * @param {Object} config Configuration options
15912  */
15913 Roo.menu.ColorMenu = function(config){
15914     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15915     this.plain = true;
15916     var ci = new Roo.menu.ColorItem(config);
15917     this.add(ci);
15918     /**
15919      * The {@link Roo.ColorPalette} instance for this ColorMenu
15920      * @type ColorPalette
15921      */
15922     this.palette = ci.palette;
15923     /**
15924      * @event select
15925      * @param {ColorPalette} palette
15926      * @param {String} color
15927      */
15928     this.relayEvents(ci, ["select"]);
15929 };
15930 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15931  * Based on:
15932  * Ext JS Library 1.1.1
15933  * Copyright(c) 2006-2007, Ext JS, LLC.
15934  *
15935  * Originally Released Under LGPL - original licence link has changed is not relivant.
15936  *
15937  * Fork - LGPL
15938  * <script type="text/javascript">
15939  */
15940  
15941 /**
15942  * @class Roo.form.TextItem
15943  * @extends Roo.BoxComponent
15944  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15945  * @constructor
15946  * Creates a new TextItem
15947  * @param {Object} config Configuration options
15948  */
15949 Roo.form.TextItem = function(config){
15950     Roo.form.TextItem.superclass.constructor.call(this, config);
15951 };
15952
15953 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15954     
15955     /**
15956      * @cfg {String} tag the tag for this item (default div)
15957      */
15958     tag : 'div',
15959     /**
15960      * @cfg {String} html the content for this item
15961      */
15962     html : '',
15963     
15964     getAutoCreate : function()
15965     {
15966         var cfg = {
15967             id: this.id,
15968             tag: this.tag,
15969             html: this.html,
15970             cls: 'x-form-item'
15971         };
15972         
15973         return cfg;
15974         
15975     },
15976     
15977     onRender : function(ct, position)
15978     {
15979         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15980         
15981         if(!this.el){
15982             var cfg = this.getAutoCreate();
15983             if(!cfg.name){
15984                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15985             }
15986             if (!cfg.name.length) {
15987                 delete cfg.name;
15988             }
15989             this.el = ct.createChild(cfg, position);
15990         }
15991     },
15992     /*
15993      * setHTML
15994      * @param {String} html update the Contents of the element.
15995      */
15996     setHTML : function(html)
15997     {
15998         this.fieldEl.dom.innerHTML = html;
15999     }
16000     
16001 });/*
16002  * Based on:
16003  * Ext JS Library 1.1.1
16004  * Copyright(c) 2006-2007, Ext JS, LLC.
16005  *
16006  * Originally Released Under LGPL - original licence link has changed is not relivant.
16007  *
16008  * Fork - LGPL
16009  * <script type="text/javascript">
16010  */
16011  
16012 /**
16013  * @class Roo.form.Field
16014  * @extends Roo.BoxComponent
16015  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16016  * @constructor
16017  * Creates a new Field
16018  * @param {Object} config Configuration options
16019  */
16020 Roo.form.Field = function(config){
16021     Roo.form.Field.superclass.constructor.call(this, config);
16022 };
16023
16024 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16025     /**
16026      * @cfg {String} fieldLabel Label to use when rendering a form.
16027      */
16028        /**
16029      * @cfg {String} qtip Mouse over tip
16030      */
16031      
16032     /**
16033      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16034      */
16035     invalidClass : "x-form-invalid",
16036     /**
16037      * @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")
16038      */
16039     invalidText : "The value in this field is invalid",
16040     /**
16041      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16042      */
16043     focusClass : "x-form-focus",
16044     /**
16045      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16046       automatic validation (defaults to "keyup").
16047      */
16048     validationEvent : "keyup",
16049     /**
16050      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16051      */
16052     validateOnBlur : true,
16053     /**
16054      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16055      */
16056     validationDelay : 250,
16057     /**
16058      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16059      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16060      */
16061     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16062     /**
16063      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16064      */
16065     fieldClass : "x-form-field",
16066     /**
16067      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16068      *<pre>
16069 Value         Description
16070 -----------   ----------------------------------------------------------------------
16071 qtip          Display a quick tip when the user hovers over the field
16072 title         Display a default browser title attribute popup
16073 under         Add a block div beneath the field containing the error text
16074 side          Add an error icon to the right of the field with a popup on hover
16075 [element id]  Add the error text directly to the innerHTML of the specified element
16076 </pre>
16077      */
16078     msgTarget : 'qtip',
16079     /**
16080      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16081      */
16082     msgFx : 'normal',
16083
16084     /**
16085      * @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.
16086      */
16087     readOnly : false,
16088
16089     /**
16090      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16091      */
16092     disabled : false,
16093
16094     /**
16095      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16096      */
16097     inputType : undefined,
16098     
16099     /**
16100      * @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).
16101          */
16102         tabIndex : undefined,
16103         
16104     // private
16105     isFormField : true,
16106
16107     // private
16108     hasFocus : false,
16109     /**
16110      * @property {Roo.Element} fieldEl
16111      * Element Containing the rendered Field (with label etc.)
16112      */
16113     /**
16114      * @cfg {Mixed} value A value to initialize this field with.
16115      */
16116     value : undefined,
16117
16118     /**
16119      * @cfg {String} name The field's HTML name attribute.
16120      */
16121     /**
16122      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16123      */
16124     // private
16125     loadedValue : false,
16126      
16127      
16128         // private ??
16129         initComponent : function(){
16130         Roo.form.Field.superclass.initComponent.call(this);
16131         this.addEvents({
16132             /**
16133              * @event focus
16134              * Fires when this field receives input focus.
16135              * @param {Roo.form.Field} this
16136              */
16137             focus : true,
16138             /**
16139              * @event blur
16140              * Fires when this field loses input focus.
16141              * @param {Roo.form.Field} this
16142              */
16143             blur : true,
16144             /**
16145              * @event specialkey
16146              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16147              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16148              * @param {Roo.form.Field} this
16149              * @param {Roo.EventObject} e The event object
16150              */
16151             specialkey : true,
16152             /**
16153              * @event change
16154              * Fires just before the field blurs if the field value has changed.
16155              * @param {Roo.form.Field} this
16156              * @param {Mixed} newValue The new value
16157              * @param {Mixed} oldValue The original value
16158              */
16159             change : true,
16160             /**
16161              * @event invalid
16162              * Fires after the field has been marked as invalid.
16163              * @param {Roo.form.Field} this
16164              * @param {String} msg The validation message
16165              */
16166             invalid : true,
16167             /**
16168              * @event valid
16169              * Fires after the field has been validated with no errors.
16170              * @param {Roo.form.Field} this
16171              */
16172             valid : true,
16173              /**
16174              * @event keyup
16175              * Fires after the key up
16176              * @param {Roo.form.Field} this
16177              * @param {Roo.EventObject}  e The event Object
16178              */
16179             keyup : true
16180         });
16181     },
16182
16183     /**
16184      * Returns the name attribute of the field if available
16185      * @return {String} name The field name
16186      */
16187     getName: function(){
16188          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16189     },
16190
16191     // private
16192     onRender : function(ct, position){
16193         Roo.form.Field.superclass.onRender.call(this, ct, position);
16194         if(!this.el){
16195             var cfg = this.getAutoCreate();
16196             if(!cfg.name){
16197                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16198             }
16199             if (!cfg.name.length) {
16200                 delete cfg.name;
16201             }
16202             if(this.inputType){
16203                 cfg.type = this.inputType;
16204             }
16205             this.el = ct.createChild(cfg, position);
16206         }
16207         var type = this.el.dom.type;
16208         if(type){
16209             if(type == 'password'){
16210                 type = 'text';
16211             }
16212             this.el.addClass('x-form-'+type);
16213         }
16214         if(this.readOnly){
16215             this.el.dom.readOnly = true;
16216         }
16217         if(this.tabIndex !== undefined){
16218             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16219         }
16220
16221         this.el.addClass([this.fieldClass, this.cls]);
16222         this.initValue();
16223     },
16224
16225     /**
16226      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16227      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16228      * @return {Roo.form.Field} this
16229      */
16230     applyTo : function(target){
16231         this.allowDomMove = false;
16232         this.el = Roo.get(target);
16233         this.render(this.el.dom.parentNode);
16234         return this;
16235     },
16236
16237     // private
16238     initValue : function(){
16239         if(this.value !== undefined){
16240             this.setValue(this.value);
16241         }else if(this.el.dom.value.length > 0){
16242             this.setValue(this.el.dom.value);
16243         }
16244     },
16245
16246     /**
16247      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16248      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16249      */
16250     isDirty : function() {
16251         if(this.disabled) {
16252             return false;
16253         }
16254         return String(this.getValue()) !== String(this.originalValue);
16255     },
16256
16257     /**
16258      * stores the current value in loadedValue
16259      */
16260     resetHasChanged : function()
16261     {
16262         this.loadedValue = String(this.getValue());
16263     },
16264     /**
16265      * checks the current value against the 'loaded' value.
16266      * Note - will return false if 'resetHasChanged' has not been called first.
16267      */
16268     hasChanged : function()
16269     {
16270         if(this.disabled || this.readOnly) {
16271             return false;
16272         }
16273         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16274     },
16275     
16276     
16277     
16278     // private
16279     afterRender : function(){
16280         Roo.form.Field.superclass.afterRender.call(this);
16281         this.initEvents();
16282     },
16283
16284     // private
16285     fireKey : function(e){
16286         //Roo.log('field ' + e.getKey());
16287         if(e.isNavKeyPress()){
16288             this.fireEvent("specialkey", this, e);
16289         }
16290     },
16291
16292     /**
16293      * Resets the current field value to the originally loaded value and clears any validation messages
16294      */
16295     reset : function(){
16296         this.setValue(this.resetValue);
16297         this.originalValue = this.getValue();
16298         this.clearInvalid();
16299     },
16300
16301     // private
16302     initEvents : function(){
16303         // safari killled keypress - so keydown is now used..
16304         this.el.on("keydown" , this.fireKey,  this);
16305         this.el.on("focus", this.onFocus,  this);
16306         this.el.on("blur", this.onBlur,  this);
16307         this.el.relayEvent('keyup', this);
16308
16309         // reference to original value for reset
16310         this.originalValue = this.getValue();
16311         this.resetValue =  this.getValue();
16312     },
16313
16314     // private
16315     onFocus : function(){
16316         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16317             this.el.addClass(this.focusClass);
16318         }
16319         if(!this.hasFocus){
16320             this.hasFocus = true;
16321             this.startValue = this.getValue();
16322             this.fireEvent("focus", this);
16323         }
16324     },
16325
16326     beforeBlur : Roo.emptyFn,
16327
16328     // private
16329     onBlur : function(){
16330         this.beforeBlur();
16331         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16332             this.el.removeClass(this.focusClass);
16333         }
16334         this.hasFocus = false;
16335         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16336             this.validate();
16337         }
16338         var v = this.getValue();
16339         if(String(v) !== String(this.startValue)){
16340             this.fireEvent('change', this, v, this.startValue);
16341         }
16342         this.fireEvent("blur", this);
16343     },
16344
16345     /**
16346      * Returns whether or not the field value is currently valid
16347      * @param {Boolean} preventMark True to disable marking the field invalid
16348      * @return {Boolean} True if the value is valid, else false
16349      */
16350     isValid : function(preventMark){
16351         if(this.disabled){
16352             return true;
16353         }
16354         var restore = this.preventMark;
16355         this.preventMark = preventMark === true;
16356         var v = this.validateValue(this.processValue(this.getRawValue()));
16357         this.preventMark = restore;
16358         return v;
16359     },
16360
16361     /**
16362      * Validates the field value
16363      * @return {Boolean} True if the value is valid, else false
16364      */
16365     validate : function(){
16366         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16367             this.clearInvalid();
16368             return true;
16369         }
16370         return false;
16371     },
16372
16373     processValue : function(value){
16374         return value;
16375     },
16376
16377     // private
16378     // Subclasses should provide the validation implementation by overriding this
16379     validateValue : function(value){
16380         return true;
16381     },
16382
16383     /**
16384      * Mark this field as invalid
16385      * @param {String} msg The validation message
16386      */
16387     markInvalid : function(msg){
16388         if(!this.rendered || this.preventMark){ // not rendered
16389             return;
16390         }
16391         
16392         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16393         
16394         obj.el.addClass(this.invalidClass);
16395         msg = msg || this.invalidText;
16396         switch(this.msgTarget){
16397             case 'qtip':
16398                 obj.el.dom.qtip = msg;
16399                 obj.el.dom.qclass = 'x-form-invalid-tip';
16400                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16401                     Roo.QuickTips.enable();
16402                 }
16403                 break;
16404             case 'title':
16405                 this.el.dom.title = msg;
16406                 break;
16407             case 'under':
16408                 if(!this.errorEl){
16409                     var elp = this.el.findParent('.x-form-element', 5, true);
16410                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16411                     this.errorEl.setWidth(elp.getWidth(true)-20);
16412                 }
16413                 this.errorEl.update(msg);
16414                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16415                 break;
16416             case 'side':
16417                 if(!this.errorIcon){
16418                     var elp = this.el.findParent('.x-form-element', 5, true);
16419                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16420                 }
16421                 this.alignErrorIcon();
16422                 this.errorIcon.dom.qtip = msg;
16423                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16424                 this.errorIcon.show();
16425                 this.on('resize', this.alignErrorIcon, this);
16426                 break;
16427             default:
16428                 var t = Roo.getDom(this.msgTarget);
16429                 t.innerHTML = msg;
16430                 t.style.display = this.msgDisplay;
16431                 break;
16432         }
16433         this.fireEvent('invalid', this, msg);
16434     },
16435
16436     // private
16437     alignErrorIcon : function(){
16438         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16439     },
16440
16441     /**
16442      * Clear any invalid styles/messages for this field
16443      */
16444     clearInvalid : function(){
16445         if(!this.rendered || this.preventMark){ // not rendered
16446             return;
16447         }
16448         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16449         
16450         obj.el.removeClass(this.invalidClass);
16451         switch(this.msgTarget){
16452             case 'qtip':
16453                 obj.el.dom.qtip = '';
16454                 break;
16455             case 'title':
16456                 this.el.dom.title = '';
16457                 break;
16458             case 'under':
16459                 if(this.errorEl){
16460                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16461                 }
16462                 break;
16463             case 'side':
16464                 if(this.errorIcon){
16465                     this.errorIcon.dom.qtip = '';
16466                     this.errorIcon.hide();
16467                     this.un('resize', this.alignErrorIcon, this);
16468                 }
16469                 break;
16470             default:
16471                 var t = Roo.getDom(this.msgTarget);
16472                 t.innerHTML = '';
16473                 t.style.display = 'none';
16474                 break;
16475         }
16476         this.fireEvent('valid', this);
16477     },
16478
16479     /**
16480      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16481      * @return {Mixed} value The field value
16482      */
16483     getRawValue : function(){
16484         var v = this.el.getValue();
16485         
16486         return v;
16487     },
16488
16489     /**
16490      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16491      * @return {Mixed} value The field value
16492      */
16493     getValue : function(){
16494         var v = this.el.getValue();
16495          
16496         return v;
16497     },
16498
16499     /**
16500      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16501      * @param {Mixed} value The value to set
16502      */
16503     setRawValue : function(v){
16504         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16505     },
16506
16507     /**
16508      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16509      * @param {Mixed} value The value to set
16510      */
16511     setValue : function(v){
16512         this.value = v;
16513         if(this.rendered){
16514             this.el.dom.value = (v === null || v === undefined ? '' : v);
16515              this.validate();
16516         }
16517     },
16518
16519     adjustSize : function(w, h){
16520         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16521         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16522         return s;
16523     },
16524
16525     adjustWidth : function(tag, w){
16526         tag = tag.toLowerCase();
16527         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16528             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16529                 if(tag == 'input'){
16530                     return w + 2;
16531                 }
16532                 if(tag == 'textarea'){
16533                     return w-2;
16534                 }
16535             }else if(Roo.isOpera){
16536                 if(tag == 'input'){
16537                     return w + 2;
16538                 }
16539                 if(tag == 'textarea'){
16540                     return w-2;
16541                 }
16542             }
16543         }
16544         return w;
16545     }
16546 });
16547
16548
16549 // anything other than normal should be considered experimental
16550 Roo.form.Field.msgFx = {
16551     normal : {
16552         show: function(msgEl, f){
16553             msgEl.setDisplayed('block');
16554         },
16555
16556         hide : function(msgEl, f){
16557             msgEl.setDisplayed(false).update('');
16558         }
16559     },
16560
16561     slide : {
16562         show: function(msgEl, f){
16563             msgEl.slideIn('t', {stopFx:true});
16564         },
16565
16566         hide : function(msgEl, f){
16567             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16568         }
16569     },
16570
16571     slideRight : {
16572         show: function(msgEl, f){
16573             msgEl.fixDisplay();
16574             msgEl.alignTo(f.el, 'tl-tr');
16575             msgEl.slideIn('l', {stopFx:true});
16576         },
16577
16578         hide : function(msgEl, f){
16579             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16580         }
16581     }
16582 };/*
16583  * Based on:
16584  * Ext JS Library 1.1.1
16585  * Copyright(c) 2006-2007, Ext JS, LLC.
16586  *
16587  * Originally Released Under LGPL - original licence link has changed is not relivant.
16588  *
16589  * Fork - LGPL
16590  * <script type="text/javascript">
16591  */
16592  
16593
16594 /**
16595  * @class Roo.form.TextField
16596  * @extends Roo.form.Field
16597  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16598  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16599  * @constructor
16600  * Creates a new TextField
16601  * @param {Object} config Configuration options
16602  */
16603 Roo.form.TextField = function(config){
16604     Roo.form.TextField.superclass.constructor.call(this, config);
16605     this.addEvents({
16606         /**
16607          * @event autosize
16608          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16609          * according to the default logic, but this event provides a hook for the developer to apply additional
16610          * logic at runtime to resize the field if needed.
16611              * @param {Roo.form.Field} this This text field
16612              * @param {Number} width The new field width
16613              */
16614         autosize : true
16615     });
16616 };
16617
16618 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16619     /**
16620      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16621      */
16622     grow : false,
16623     /**
16624      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16625      */
16626     growMin : 30,
16627     /**
16628      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16629      */
16630     growMax : 800,
16631     /**
16632      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16633      */
16634     vtype : null,
16635     /**
16636      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16637      */
16638     maskRe : null,
16639     /**
16640      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16641      */
16642     disableKeyFilter : false,
16643     /**
16644      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16645      */
16646     allowBlank : true,
16647     /**
16648      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16649      */
16650     minLength : 0,
16651     /**
16652      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16653      */
16654     maxLength : Number.MAX_VALUE,
16655     /**
16656      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16657      */
16658     minLengthText : "The minimum length for this field is {0}",
16659     /**
16660      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16661      */
16662     maxLengthText : "The maximum length for this field is {0}",
16663     /**
16664      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16665      */
16666     selectOnFocus : false,
16667     /**
16668      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16669      */    
16670     allowLeadingSpace : false,
16671     /**
16672      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16673      */
16674     blankText : "This field is required",
16675     /**
16676      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16677      * If available, this function will be called only after the basic validators all return true, and will be passed the
16678      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16679      */
16680     validator : null,
16681     /**
16682      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16683      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16684      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16685      */
16686     regex : null,
16687     /**
16688      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16689      */
16690     regexText : "",
16691     /**
16692      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16693      */
16694     emptyText : null,
16695    
16696
16697     // private
16698     initEvents : function()
16699     {
16700         if (this.emptyText) {
16701             this.el.attr('placeholder', this.emptyText);
16702         }
16703         
16704         Roo.form.TextField.superclass.initEvents.call(this);
16705         if(this.validationEvent == 'keyup'){
16706             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16707             this.el.on('keyup', this.filterValidation, this);
16708         }
16709         else if(this.validationEvent !== false){
16710             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16711         }
16712         
16713         if(this.selectOnFocus){
16714             this.on("focus", this.preFocus, this);
16715         }
16716         if (!this.allowLeadingSpace) {
16717             this.on('blur', this.cleanLeadingSpace, this);
16718         }
16719         
16720         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16721             this.el.on("keypress", this.filterKeys, this);
16722         }
16723         if(this.grow){
16724             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16725             this.el.on("click", this.autoSize,  this);
16726         }
16727         if(this.el.is('input[type=password]') && Roo.isSafari){
16728             this.el.on('keydown', this.SafariOnKeyDown, this);
16729         }
16730     },
16731
16732     processValue : function(value){
16733         if(this.stripCharsRe){
16734             var newValue = value.replace(this.stripCharsRe, '');
16735             if(newValue !== value){
16736                 this.setRawValue(newValue);
16737                 return newValue;
16738             }
16739         }
16740         return value;
16741     },
16742
16743     filterValidation : function(e){
16744         if(!e.isNavKeyPress()){
16745             this.validationTask.delay(this.validationDelay);
16746         }
16747     },
16748
16749     // private
16750     onKeyUp : function(e){
16751         if(!e.isNavKeyPress()){
16752             this.autoSize();
16753         }
16754     },
16755     // private - clean the leading white space
16756     cleanLeadingSpace : function(e)
16757     {
16758         if ( this.inputType == 'file') {
16759             return;
16760         }
16761         
16762         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16763     },
16764     /**
16765      * Resets the current field value to the originally-loaded value and clears any validation messages.
16766      *  
16767      */
16768     reset : function(){
16769         Roo.form.TextField.superclass.reset.call(this);
16770        
16771     }, 
16772     // private
16773     preFocus : function(){
16774         
16775         if(this.selectOnFocus){
16776             this.el.dom.select();
16777         }
16778     },
16779
16780     
16781     // private
16782     filterKeys : function(e){
16783         var k = e.getKey();
16784         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16785             return;
16786         }
16787         var c = e.getCharCode(), cc = String.fromCharCode(c);
16788         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16789             return;
16790         }
16791         if(!this.maskRe.test(cc)){
16792             e.stopEvent();
16793         }
16794     },
16795
16796     setValue : function(v){
16797         
16798         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16799         
16800         this.autoSize();
16801     },
16802
16803     /**
16804      * Validates a value according to the field's validation rules and marks the field as invalid
16805      * if the validation fails
16806      * @param {Mixed} value The value to validate
16807      * @return {Boolean} True if the value is valid, else false
16808      */
16809     validateValue : function(value){
16810         if(value.length < 1)  { // if it's blank
16811              if(this.allowBlank){
16812                 this.clearInvalid();
16813                 return true;
16814              }else{
16815                 this.markInvalid(this.blankText);
16816                 return false;
16817              }
16818         }
16819         if(value.length < this.minLength){
16820             this.markInvalid(String.format(this.minLengthText, this.minLength));
16821             return false;
16822         }
16823         if(value.length > this.maxLength){
16824             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16825             return false;
16826         }
16827         if(this.vtype){
16828             var vt = Roo.form.VTypes;
16829             if(!vt[this.vtype](value, this)){
16830                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16831                 return false;
16832             }
16833         }
16834         if(typeof this.validator == "function"){
16835             var msg = this.validator(value);
16836             if(msg !== true){
16837                 this.markInvalid(msg);
16838                 return false;
16839             }
16840         }
16841         if(this.regex && !this.regex.test(value)){
16842             this.markInvalid(this.regexText);
16843             return false;
16844         }
16845         return true;
16846     },
16847
16848     /**
16849      * Selects text in this field
16850      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16851      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16852      */
16853     selectText : function(start, end){
16854         var v = this.getRawValue();
16855         if(v.length > 0){
16856             start = start === undefined ? 0 : start;
16857             end = end === undefined ? v.length : end;
16858             var d = this.el.dom;
16859             if(d.setSelectionRange){
16860                 d.setSelectionRange(start, end);
16861             }else if(d.createTextRange){
16862                 var range = d.createTextRange();
16863                 range.moveStart("character", start);
16864                 range.moveEnd("character", v.length-end);
16865                 range.select();
16866             }
16867         }
16868     },
16869
16870     /**
16871      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16872      * This only takes effect if grow = true, and fires the autosize event.
16873      */
16874     autoSize : function(){
16875         if(!this.grow || !this.rendered){
16876             return;
16877         }
16878         if(!this.metrics){
16879             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16880         }
16881         var el = this.el;
16882         var v = el.dom.value;
16883         var d = document.createElement('div');
16884         d.appendChild(document.createTextNode(v));
16885         v = d.innerHTML;
16886         d = null;
16887         v += "&#160;";
16888         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16889         this.el.setWidth(w);
16890         this.fireEvent("autosize", this, w);
16891     },
16892     
16893     // private
16894     SafariOnKeyDown : function(event)
16895     {
16896         // this is a workaround for a password hang bug on chrome/ webkit.
16897         
16898         var isSelectAll = false;
16899         
16900         if(this.el.dom.selectionEnd > 0){
16901             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16902         }
16903         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16904             event.preventDefault();
16905             this.setValue('');
16906             return;
16907         }
16908         
16909         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16910             
16911             event.preventDefault();
16912             // this is very hacky as keydown always get's upper case.
16913             
16914             var cc = String.fromCharCode(event.getCharCode());
16915             
16916             
16917             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16918             
16919         }
16920         
16921         
16922     }
16923 });/*
16924  * Based on:
16925  * Ext JS Library 1.1.1
16926  * Copyright(c) 2006-2007, Ext JS, LLC.
16927  *
16928  * Originally Released Under LGPL - original licence link has changed is not relivant.
16929  *
16930  * Fork - LGPL
16931  * <script type="text/javascript">
16932  */
16933  
16934 /**
16935  * @class Roo.form.Hidden
16936  * @extends Roo.form.TextField
16937  * Simple Hidden element used on forms 
16938  * 
16939  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16940  * 
16941  * @constructor
16942  * Creates a new Hidden form element.
16943  * @param {Object} config Configuration options
16944  */
16945
16946
16947
16948 // easy hidden field...
16949 Roo.form.Hidden = function(config){
16950     Roo.form.Hidden.superclass.constructor.call(this, config);
16951 };
16952   
16953 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16954     fieldLabel:      '',
16955     inputType:      'hidden',
16956     width:          50,
16957     allowBlank:     true,
16958     labelSeparator: '',
16959     hidden:         true,
16960     itemCls :       'x-form-item-display-none'
16961
16962
16963 });
16964
16965
16966 /*
16967  * Based on:
16968  * Ext JS Library 1.1.1
16969  * Copyright(c) 2006-2007, Ext JS, LLC.
16970  *
16971  * Originally Released Under LGPL - original licence link has changed is not relivant.
16972  *
16973  * Fork - LGPL
16974  * <script type="text/javascript">
16975  */
16976  
16977 /**
16978  * @class Roo.form.TriggerField
16979  * @extends Roo.form.TextField
16980  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16981  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16982  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16983  * for which you can provide a custom implementation.  For example:
16984  * <pre><code>
16985 var trigger = new Roo.form.TriggerField();
16986 trigger.onTriggerClick = myTriggerFn;
16987 trigger.applyTo('my-field');
16988 </code></pre>
16989  *
16990  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16991  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16992  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16993  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16994  * @constructor
16995  * Create a new TriggerField.
16996  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16997  * to the base TextField)
16998  */
16999 Roo.form.TriggerField = function(config){
17000     this.mimicing = false;
17001     Roo.form.TriggerField.superclass.constructor.call(this, config);
17002 };
17003
17004 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17005     /**
17006      * @cfg {String} triggerClass A CSS class to apply to the trigger
17007      */
17008     /**
17009      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17010      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17011      */
17012     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17013     /**
17014      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17015      */
17016     hideTrigger:false,
17017
17018     /** @cfg {Boolean} grow @hide */
17019     /** @cfg {Number} growMin @hide */
17020     /** @cfg {Number} growMax @hide */
17021
17022     /**
17023      * @hide 
17024      * @method
17025      */
17026     autoSize: Roo.emptyFn,
17027     // private
17028     monitorTab : true,
17029     // private
17030     deferHeight : true,
17031
17032     
17033     actionMode : 'wrap',
17034     // private
17035     onResize : function(w, h){
17036         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17037         if(typeof w == 'number'){
17038             var x = w - this.trigger.getWidth();
17039             this.el.setWidth(this.adjustWidth('input', x));
17040             this.trigger.setStyle('left', x+'px');
17041         }
17042     },
17043
17044     // private
17045     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17046
17047     // private
17048     getResizeEl : function(){
17049         return this.wrap;
17050     },
17051
17052     // private
17053     getPositionEl : function(){
17054         return this.wrap;
17055     },
17056
17057     // private
17058     alignErrorIcon : function(){
17059         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17060     },
17061
17062     // private
17063     onRender : function(ct, position){
17064         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17065         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17066         this.trigger = this.wrap.createChild(this.triggerConfig ||
17067                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17068         if(this.hideTrigger){
17069             this.trigger.setDisplayed(false);
17070         }
17071         this.initTrigger();
17072         if(!this.width){
17073             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17074         }
17075     },
17076
17077     // private
17078     initTrigger : function(){
17079         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17080         this.trigger.addClassOnOver('x-form-trigger-over');
17081         this.trigger.addClassOnClick('x-form-trigger-click');
17082     },
17083
17084     // private
17085     onDestroy : function(){
17086         if(this.trigger){
17087             this.trigger.removeAllListeners();
17088             this.trigger.remove();
17089         }
17090         if(this.wrap){
17091             this.wrap.remove();
17092         }
17093         Roo.form.TriggerField.superclass.onDestroy.call(this);
17094     },
17095
17096     // private
17097     onFocus : function(){
17098         Roo.form.TriggerField.superclass.onFocus.call(this);
17099         if(!this.mimicing){
17100             this.wrap.addClass('x-trigger-wrap-focus');
17101             this.mimicing = true;
17102             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17103             if(this.monitorTab){
17104                 this.el.on("keydown", this.checkTab, this);
17105             }
17106         }
17107     },
17108
17109     // private
17110     checkTab : function(e){
17111         if(e.getKey() == e.TAB){
17112             this.triggerBlur();
17113         }
17114     },
17115
17116     // private
17117     onBlur : function(){
17118         // do nothing
17119     },
17120
17121     // private
17122     mimicBlur : function(e, t){
17123         if(!this.wrap.contains(t) && this.validateBlur()){
17124             this.triggerBlur();
17125         }
17126     },
17127
17128     // private
17129     triggerBlur : function(){
17130         this.mimicing = false;
17131         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17132         if(this.monitorTab){
17133             this.el.un("keydown", this.checkTab, this);
17134         }
17135         this.wrap.removeClass('x-trigger-wrap-focus');
17136         Roo.form.TriggerField.superclass.onBlur.call(this);
17137     },
17138
17139     // private
17140     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17141     validateBlur : function(e, t){
17142         return true;
17143     },
17144
17145     // private
17146     onDisable : function(){
17147         Roo.form.TriggerField.superclass.onDisable.call(this);
17148         if(this.wrap){
17149             this.wrap.addClass('x-item-disabled');
17150         }
17151     },
17152
17153     // private
17154     onEnable : function(){
17155         Roo.form.TriggerField.superclass.onEnable.call(this);
17156         if(this.wrap){
17157             this.wrap.removeClass('x-item-disabled');
17158         }
17159     },
17160
17161     // private
17162     onShow : function(){
17163         var ae = this.getActionEl();
17164         
17165         if(ae){
17166             ae.dom.style.display = '';
17167             ae.dom.style.visibility = 'visible';
17168         }
17169     },
17170
17171     // private
17172     
17173     onHide : function(){
17174         var ae = this.getActionEl();
17175         ae.dom.style.display = 'none';
17176     },
17177
17178     /**
17179      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17180      * by an implementing function.
17181      * @method
17182      * @param {EventObject} e
17183      */
17184     onTriggerClick : Roo.emptyFn
17185 });
17186
17187 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17188 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17189 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17190 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17191     initComponent : function(){
17192         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17193
17194         this.triggerConfig = {
17195             tag:'span', cls:'x-form-twin-triggers', cn:[
17196             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17197             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17198         ]};
17199     },
17200
17201     getTrigger : function(index){
17202         return this.triggers[index];
17203     },
17204
17205     initTrigger : function(){
17206         var ts = this.trigger.select('.x-form-trigger', true);
17207         this.wrap.setStyle('overflow', 'hidden');
17208         var triggerField = this;
17209         ts.each(function(t, all, index){
17210             t.hide = function(){
17211                 var w = triggerField.wrap.getWidth();
17212                 this.dom.style.display = 'none';
17213                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17214             };
17215             t.show = function(){
17216                 var w = triggerField.wrap.getWidth();
17217                 this.dom.style.display = '';
17218                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17219             };
17220             var triggerIndex = 'Trigger'+(index+1);
17221
17222             if(this['hide'+triggerIndex]){
17223                 t.dom.style.display = 'none';
17224             }
17225             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17226             t.addClassOnOver('x-form-trigger-over');
17227             t.addClassOnClick('x-form-trigger-click');
17228         }, this);
17229         this.triggers = ts.elements;
17230     },
17231
17232     onTrigger1Click : Roo.emptyFn,
17233     onTrigger2Click : Roo.emptyFn
17234 });/*
17235  * Based on:
17236  * Ext JS Library 1.1.1
17237  * Copyright(c) 2006-2007, Ext JS, LLC.
17238  *
17239  * Originally Released Under LGPL - original licence link has changed is not relivant.
17240  *
17241  * Fork - LGPL
17242  * <script type="text/javascript">
17243  */
17244  
17245 /**
17246  * @class Roo.form.TextArea
17247  * @extends Roo.form.TextField
17248  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17249  * support for auto-sizing.
17250  * @constructor
17251  * Creates a new TextArea
17252  * @param {Object} config Configuration options
17253  */
17254 Roo.form.TextArea = function(config){
17255     Roo.form.TextArea.superclass.constructor.call(this, config);
17256     // these are provided exchanges for backwards compat
17257     // minHeight/maxHeight were replaced by growMin/growMax to be
17258     // compatible with TextField growing config values
17259     if(this.minHeight !== undefined){
17260         this.growMin = this.minHeight;
17261     }
17262     if(this.maxHeight !== undefined){
17263         this.growMax = this.maxHeight;
17264     }
17265 };
17266
17267 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17268     /**
17269      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17270      */
17271     growMin : 60,
17272     /**
17273      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17274      */
17275     growMax: 1000,
17276     /**
17277      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17278      * in the field (equivalent to setting overflow: hidden, defaults to false)
17279      */
17280     preventScrollbars: false,
17281     /**
17282      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17283      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17284      */
17285
17286     // private
17287     onRender : function(ct, position){
17288         if(!this.el){
17289             this.defaultAutoCreate = {
17290                 tag: "textarea",
17291                 style:"width:300px;height:60px;",
17292                 autocomplete: "new-password"
17293             };
17294         }
17295         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17296         if(this.grow){
17297             this.textSizeEl = Roo.DomHelper.append(document.body, {
17298                 tag: "pre", cls: "x-form-grow-sizer"
17299             });
17300             if(this.preventScrollbars){
17301                 this.el.setStyle("overflow", "hidden");
17302             }
17303             this.el.setHeight(this.growMin);
17304         }
17305     },
17306
17307     onDestroy : function(){
17308         if(this.textSizeEl){
17309             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17310         }
17311         Roo.form.TextArea.superclass.onDestroy.call(this);
17312     },
17313
17314     // private
17315     onKeyUp : function(e){
17316         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17317             this.autoSize();
17318         }
17319     },
17320
17321     /**
17322      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17323      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17324      */
17325     autoSize : function(){
17326         if(!this.grow || !this.textSizeEl){
17327             return;
17328         }
17329         var el = this.el;
17330         var v = el.dom.value;
17331         var ts = this.textSizeEl;
17332
17333         ts.innerHTML = '';
17334         ts.appendChild(document.createTextNode(v));
17335         v = ts.innerHTML;
17336
17337         Roo.fly(ts).setWidth(this.el.getWidth());
17338         if(v.length < 1){
17339             v = "&#160;&#160;";
17340         }else{
17341             if(Roo.isIE){
17342                 v = v.replace(/\n/g, '<p>&#160;</p>');
17343             }
17344             v += "&#160;\n&#160;";
17345         }
17346         ts.innerHTML = v;
17347         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17348         if(h != this.lastHeight){
17349             this.lastHeight = h;
17350             this.el.setHeight(h);
17351             this.fireEvent("autosize", this, h);
17352         }
17353     }
17354 });/*
17355  * Based on:
17356  * Ext JS Library 1.1.1
17357  * Copyright(c) 2006-2007, Ext JS, LLC.
17358  *
17359  * Originally Released Under LGPL - original licence link has changed is not relivant.
17360  *
17361  * Fork - LGPL
17362  * <script type="text/javascript">
17363  */
17364  
17365
17366 /**
17367  * @class Roo.form.NumberField
17368  * @extends Roo.form.TextField
17369  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17370  * @constructor
17371  * Creates a new NumberField
17372  * @param {Object} config Configuration options
17373  */
17374 Roo.form.NumberField = function(config){
17375     Roo.form.NumberField.superclass.constructor.call(this, config);
17376 };
17377
17378 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17379     /**
17380      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17381      */
17382     fieldClass: "x-form-field x-form-num-field",
17383     /**
17384      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17385      */
17386     allowDecimals : true,
17387     /**
17388      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17389      */
17390     decimalSeparator : ".",
17391     /**
17392      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17393      */
17394     decimalPrecision : 2,
17395     /**
17396      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17397      */
17398     allowNegative : true,
17399     /**
17400      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17401      */
17402     minValue : Number.NEGATIVE_INFINITY,
17403     /**
17404      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17405      */
17406     maxValue : Number.MAX_VALUE,
17407     /**
17408      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17409      */
17410     minText : "The minimum value for this field is {0}",
17411     /**
17412      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17413      */
17414     maxText : "The maximum value for this field is {0}",
17415     /**
17416      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17417      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17418      */
17419     nanText : "{0} is not a valid number",
17420
17421     // private
17422     initEvents : function(){
17423         Roo.form.NumberField.superclass.initEvents.call(this);
17424         var allowed = "0123456789";
17425         if(this.allowDecimals){
17426             allowed += this.decimalSeparator;
17427         }
17428         if(this.allowNegative){
17429             allowed += "-";
17430         }
17431         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17432         var keyPress = function(e){
17433             var k = e.getKey();
17434             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17435                 return;
17436             }
17437             var c = e.getCharCode();
17438             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17439                 e.stopEvent();
17440             }
17441         };
17442         this.el.on("keypress", keyPress, this);
17443     },
17444
17445     // private
17446     validateValue : function(value){
17447         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17448             return false;
17449         }
17450         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17451              return true;
17452         }
17453         var num = this.parseValue(value);
17454         if(isNaN(num)){
17455             this.markInvalid(String.format(this.nanText, value));
17456             return false;
17457         }
17458         if(num < this.minValue){
17459             this.markInvalid(String.format(this.minText, this.minValue));
17460             return false;
17461         }
17462         if(num > this.maxValue){
17463             this.markInvalid(String.format(this.maxText, this.maxValue));
17464             return false;
17465         }
17466         return true;
17467     },
17468
17469     getValue : function(){
17470         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17471     },
17472
17473     // private
17474     parseValue : function(value){
17475         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17476         return isNaN(value) ? '' : value;
17477     },
17478
17479     // private
17480     fixPrecision : function(value){
17481         var nan = isNaN(value);
17482         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17483             return nan ? '' : value;
17484         }
17485         return parseFloat(value).toFixed(this.decimalPrecision);
17486     },
17487
17488     setValue : function(v){
17489         v = this.fixPrecision(v);
17490         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17491     },
17492
17493     // private
17494     decimalPrecisionFcn : function(v){
17495         return Math.floor(v);
17496     },
17497
17498     beforeBlur : function(){
17499         var v = this.parseValue(this.getRawValue());
17500         if(v){
17501             this.setValue(v);
17502         }
17503     }
17504 });/*
17505  * Based on:
17506  * Ext JS Library 1.1.1
17507  * Copyright(c) 2006-2007, Ext JS, LLC.
17508  *
17509  * Originally Released Under LGPL - original licence link has changed is not relivant.
17510  *
17511  * Fork - LGPL
17512  * <script type="text/javascript">
17513  */
17514  
17515 /**
17516  * @class Roo.form.DateField
17517  * @extends Roo.form.TriggerField
17518  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17519 * @constructor
17520 * Create a new DateField
17521 * @param {Object} config
17522  */
17523 Roo.form.DateField = function(config)
17524 {
17525     Roo.form.DateField.superclass.constructor.call(this, config);
17526     
17527       this.addEvents({
17528          
17529         /**
17530          * @event select
17531          * Fires when a date is selected
17532              * @param {Roo.form.DateField} combo This combo box
17533              * @param {Date} date The date selected
17534              */
17535         'select' : true
17536          
17537     });
17538     
17539     
17540     if(typeof this.minValue == "string") {
17541         this.minValue = this.parseDate(this.minValue);
17542     }
17543     if(typeof this.maxValue == "string") {
17544         this.maxValue = this.parseDate(this.maxValue);
17545     }
17546     this.ddMatch = null;
17547     if(this.disabledDates){
17548         var dd = this.disabledDates;
17549         var re = "(?:";
17550         for(var i = 0; i < dd.length; i++){
17551             re += dd[i];
17552             if(i != dd.length-1) {
17553                 re += "|";
17554             }
17555         }
17556         this.ddMatch = new RegExp(re + ")");
17557     }
17558 };
17559
17560 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17561     /**
17562      * @cfg {String} format
17563      * The default date format string which can be overriden for localization support.  The format must be
17564      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17565      */
17566     format : "m/d/y",
17567     /**
17568      * @cfg {String} altFormats
17569      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17570      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17571      */
17572     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17573     /**
17574      * @cfg {Array} disabledDays
17575      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17576      */
17577     disabledDays : null,
17578     /**
17579      * @cfg {String} disabledDaysText
17580      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17581      */
17582     disabledDaysText : "Disabled",
17583     /**
17584      * @cfg {Array} disabledDates
17585      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17586      * expression so they are very powerful. Some examples:
17587      * <ul>
17588      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17589      * <li>["03/08", "09/16"] would disable those days for every year</li>
17590      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17591      * <li>["03/../2006"] would disable every day in March 2006</li>
17592      * <li>["^03"] would disable every day in every March</li>
17593      * </ul>
17594      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17595      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17596      */
17597     disabledDates : null,
17598     /**
17599      * @cfg {String} disabledDatesText
17600      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17601      */
17602     disabledDatesText : "Disabled",
17603         
17604         
17605         /**
17606      * @cfg {Date/String} zeroValue
17607      * if the date is less that this number, then the field is rendered as empty
17608      * default is 1800
17609      */
17610         zeroValue : '1800-01-01',
17611         
17612         
17613     /**
17614      * @cfg {Date/String} minValue
17615      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17616      * valid format (defaults to null).
17617      */
17618     minValue : null,
17619     /**
17620      * @cfg {Date/String} maxValue
17621      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17622      * valid format (defaults to null).
17623      */
17624     maxValue : null,
17625     /**
17626      * @cfg {String} minText
17627      * The error text to display when the date in the cell is before minValue (defaults to
17628      * 'The date in this field must be after {minValue}').
17629      */
17630     minText : "The date in this field must be equal to or after {0}",
17631     /**
17632      * @cfg {String} maxText
17633      * The error text to display when the date in the cell is after maxValue (defaults to
17634      * 'The date in this field must be before {maxValue}').
17635      */
17636     maxText : "The date in this field must be equal to or before {0}",
17637     /**
17638      * @cfg {String} invalidText
17639      * The error text to display when the date in the field is invalid (defaults to
17640      * '{value} is not a valid date - it must be in the format {format}').
17641      */
17642     invalidText : "{0} is not a valid date - it must be in the format {1}",
17643     /**
17644      * @cfg {String} triggerClass
17645      * An additional CSS class used to style the trigger button.  The trigger will always get the
17646      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17647      * which displays a calendar icon).
17648      */
17649     triggerClass : 'x-form-date-trigger',
17650     
17651
17652     /**
17653      * @cfg {Boolean} useIso
17654      * if enabled, then the date field will use a hidden field to store the 
17655      * real value as iso formated date. default (false)
17656      */ 
17657     useIso : false,
17658     /**
17659      * @cfg {String/Object} autoCreate
17660      * A DomHelper element spec, or true for a default element spec (defaults to
17661      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17662      */ 
17663     // private
17664     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17665     
17666     // private
17667     hiddenField: false,
17668     
17669     onRender : function(ct, position)
17670     {
17671         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17672         if (this.useIso) {
17673             //this.el.dom.removeAttribute('name'); 
17674             Roo.log("Changing name?");
17675             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17676             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17677                     'before', true);
17678             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17679             // prevent input submission
17680             this.hiddenName = this.name;
17681         }
17682             
17683             
17684     },
17685     
17686     // private
17687     validateValue : function(value)
17688     {
17689         value = this.formatDate(value);
17690         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17691             Roo.log('super failed');
17692             return false;
17693         }
17694         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17695              return true;
17696         }
17697         var svalue = value;
17698         value = this.parseDate(value);
17699         if(!value){
17700             Roo.log('parse date failed' + svalue);
17701             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17702             return false;
17703         }
17704         var time = value.getTime();
17705         if(this.minValue && time < this.minValue.getTime()){
17706             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17707             return false;
17708         }
17709         if(this.maxValue && time > this.maxValue.getTime()){
17710             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17711             return false;
17712         }
17713         if(this.disabledDays){
17714             var day = value.getDay();
17715             for(var i = 0; i < this.disabledDays.length; i++) {
17716                 if(day === this.disabledDays[i]){
17717                     this.markInvalid(this.disabledDaysText);
17718                     return false;
17719                 }
17720             }
17721         }
17722         var fvalue = this.formatDate(value);
17723         if(this.ddMatch && this.ddMatch.test(fvalue)){
17724             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17725             return false;
17726         }
17727         return true;
17728     },
17729
17730     // private
17731     // Provides logic to override the default TriggerField.validateBlur which just returns true
17732     validateBlur : function(){
17733         return !this.menu || !this.menu.isVisible();
17734     },
17735     
17736     getName: function()
17737     {
17738         // returns hidden if it's set..
17739         if (!this.rendered) {return ''};
17740         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17741         
17742     },
17743
17744     /**
17745      * Returns the current date value of the date field.
17746      * @return {Date} The date value
17747      */
17748     getValue : function(){
17749         
17750         return  this.hiddenField ?
17751                 this.hiddenField.value :
17752                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17753     },
17754
17755     /**
17756      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17757      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17758      * (the default format used is "m/d/y").
17759      * <br />Usage:
17760      * <pre><code>
17761 //All of these calls set the same date value (May 4, 2006)
17762
17763 //Pass a date object:
17764 var dt = new Date('5/4/06');
17765 dateField.setValue(dt);
17766
17767 //Pass a date string (default format):
17768 dateField.setValue('5/4/06');
17769
17770 //Pass a date string (custom format):
17771 dateField.format = 'Y-m-d';
17772 dateField.setValue('2006-5-4');
17773 </code></pre>
17774      * @param {String/Date} date The date or valid date string
17775      */
17776     setValue : function(date){
17777         if (this.hiddenField) {
17778             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17779         }
17780         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17781         // make sure the value field is always stored as a date..
17782         this.value = this.parseDate(date);
17783         
17784         
17785     },
17786
17787     // private
17788     parseDate : function(value){
17789                 
17790                 if (value instanceof Date) {
17791                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17792                                 return  '';
17793                         }
17794                         return value;
17795                 }
17796                 
17797                 
17798         if(!value || value instanceof Date){
17799             return value;
17800         }
17801         var v = Date.parseDate(value, this.format);
17802          if (!v && this.useIso) {
17803             v = Date.parseDate(value, 'Y-m-d');
17804         }
17805         if(!v && this.altFormats){
17806             if(!this.altFormatsArray){
17807                 this.altFormatsArray = this.altFormats.split("|");
17808             }
17809             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17810                 v = Date.parseDate(value, this.altFormatsArray[i]);
17811             }
17812         }
17813                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17814                         v = '';
17815                 }
17816         return v;
17817     },
17818
17819     // private
17820     formatDate : function(date, fmt){
17821         return (!date || !(date instanceof Date)) ?
17822                date : date.dateFormat(fmt || this.format);
17823     },
17824
17825     // private
17826     menuListeners : {
17827         select: function(m, d){
17828             
17829             this.setValue(d);
17830             this.fireEvent('select', this, d);
17831         },
17832         show : function(){ // retain focus styling
17833             this.onFocus();
17834         },
17835         hide : function(){
17836             this.focus.defer(10, this);
17837             var ml = this.menuListeners;
17838             this.menu.un("select", ml.select,  this);
17839             this.menu.un("show", ml.show,  this);
17840             this.menu.un("hide", ml.hide,  this);
17841         }
17842     },
17843
17844     // private
17845     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17846     onTriggerClick : function(){
17847         if(this.disabled){
17848             return;
17849         }
17850         if(this.menu == null){
17851             this.menu = new Roo.menu.DateMenu();
17852         }
17853         Roo.apply(this.menu.picker,  {
17854             showClear: this.allowBlank,
17855             minDate : this.minValue,
17856             maxDate : this.maxValue,
17857             disabledDatesRE : this.ddMatch,
17858             disabledDatesText : this.disabledDatesText,
17859             disabledDays : this.disabledDays,
17860             disabledDaysText : this.disabledDaysText,
17861             format : this.useIso ? 'Y-m-d' : this.format,
17862             minText : String.format(this.minText, this.formatDate(this.minValue)),
17863             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17864         });
17865         this.menu.on(Roo.apply({}, this.menuListeners, {
17866             scope:this
17867         }));
17868         this.menu.picker.setValue(this.getValue() || new Date());
17869         this.menu.show(this.el, "tl-bl?");
17870     },
17871
17872     beforeBlur : function(){
17873         var v = this.parseDate(this.getRawValue());
17874         if(v){
17875             this.setValue(v);
17876         }
17877     },
17878
17879     /*@
17880      * overide
17881      * 
17882      */
17883     isDirty : function() {
17884         if(this.disabled) {
17885             return false;
17886         }
17887         
17888         if(typeof(this.startValue) === 'undefined'){
17889             return false;
17890         }
17891         
17892         return String(this.getValue()) !== String(this.startValue);
17893         
17894     },
17895     // @overide
17896     cleanLeadingSpace : function(e)
17897     {
17898        return;
17899     }
17900     
17901 });/*
17902  * Based on:
17903  * Ext JS Library 1.1.1
17904  * Copyright(c) 2006-2007, Ext JS, LLC.
17905  *
17906  * Originally Released Under LGPL - original licence link has changed is not relivant.
17907  *
17908  * Fork - LGPL
17909  * <script type="text/javascript">
17910  */
17911  
17912 /**
17913  * @class Roo.form.MonthField
17914  * @extends Roo.form.TriggerField
17915  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17916 * @constructor
17917 * Create a new MonthField
17918 * @param {Object} config
17919  */
17920 Roo.form.MonthField = function(config){
17921     
17922     Roo.form.MonthField.superclass.constructor.call(this, config);
17923     
17924       this.addEvents({
17925          
17926         /**
17927          * @event select
17928          * Fires when a date is selected
17929              * @param {Roo.form.MonthFieeld} combo This combo box
17930              * @param {Date} date The date selected
17931              */
17932         'select' : true
17933          
17934     });
17935     
17936     
17937     if(typeof this.minValue == "string") {
17938         this.minValue = this.parseDate(this.minValue);
17939     }
17940     if(typeof this.maxValue == "string") {
17941         this.maxValue = this.parseDate(this.maxValue);
17942     }
17943     this.ddMatch = null;
17944     if(this.disabledDates){
17945         var dd = this.disabledDates;
17946         var re = "(?:";
17947         for(var i = 0; i < dd.length; i++){
17948             re += dd[i];
17949             if(i != dd.length-1) {
17950                 re += "|";
17951             }
17952         }
17953         this.ddMatch = new RegExp(re + ")");
17954     }
17955 };
17956
17957 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17958     /**
17959      * @cfg {String} format
17960      * The default date format string which can be overriden for localization support.  The format must be
17961      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17962      */
17963     format : "M Y",
17964     /**
17965      * @cfg {String} altFormats
17966      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17967      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17968      */
17969     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17970     /**
17971      * @cfg {Array} disabledDays
17972      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17973      */
17974     disabledDays : [0,1,2,3,4,5,6],
17975     /**
17976      * @cfg {String} disabledDaysText
17977      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17978      */
17979     disabledDaysText : "Disabled",
17980     /**
17981      * @cfg {Array} disabledDates
17982      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17983      * expression so they are very powerful. Some examples:
17984      * <ul>
17985      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17986      * <li>["03/08", "09/16"] would disable those days for every year</li>
17987      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17988      * <li>["03/../2006"] would disable every day in March 2006</li>
17989      * <li>["^03"] would disable every day in every March</li>
17990      * </ul>
17991      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17992      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17993      */
17994     disabledDates : null,
17995     /**
17996      * @cfg {String} disabledDatesText
17997      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17998      */
17999     disabledDatesText : "Disabled",
18000     /**
18001      * @cfg {Date/String} minValue
18002      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18003      * valid format (defaults to null).
18004      */
18005     minValue : null,
18006     /**
18007      * @cfg {Date/String} maxValue
18008      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18009      * valid format (defaults to null).
18010      */
18011     maxValue : null,
18012     /**
18013      * @cfg {String} minText
18014      * The error text to display when the date in the cell is before minValue (defaults to
18015      * 'The date in this field must be after {minValue}').
18016      */
18017     minText : "The date in this field must be equal to or after {0}",
18018     /**
18019      * @cfg {String} maxTextf
18020      * The error text to display when the date in the cell is after maxValue (defaults to
18021      * 'The date in this field must be before {maxValue}').
18022      */
18023     maxText : "The date in this field must be equal to or before {0}",
18024     /**
18025      * @cfg {String} invalidText
18026      * The error text to display when the date in the field is invalid (defaults to
18027      * '{value} is not a valid date - it must be in the format {format}').
18028      */
18029     invalidText : "{0} is not a valid date - it must be in the format {1}",
18030     /**
18031      * @cfg {String} triggerClass
18032      * An additional CSS class used to style the trigger button.  The trigger will always get the
18033      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18034      * which displays a calendar icon).
18035      */
18036     triggerClass : 'x-form-date-trigger',
18037     
18038
18039     /**
18040      * @cfg {Boolean} useIso
18041      * if enabled, then the date field will use a hidden field to store the 
18042      * real value as iso formated date. default (true)
18043      */ 
18044     useIso : true,
18045     /**
18046      * @cfg {String/Object} autoCreate
18047      * A DomHelper element spec, or true for a default element spec (defaults to
18048      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18049      */ 
18050     // private
18051     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18052     
18053     // private
18054     hiddenField: false,
18055     
18056     hideMonthPicker : false,
18057     
18058     onRender : function(ct, position)
18059     {
18060         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18061         if (this.useIso) {
18062             this.el.dom.removeAttribute('name'); 
18063             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18064                     'before', true);
18065             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18066             // prevent input submission
18067             this.hiddenName = this.name;
18068         }
18069             
18070             
18071     },
18072     
18073     // private
18074     validateValue : function(value)
18075     {
18076         value = this.formatDate(value);
18077         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18078             return false;
18079         }
18080         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18081              return true;
18082         }
18083         var svalue = value;
18084         value = this.parseDate(value);
18085         if(!value){
18086             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18087             return false;
18088         }
18089         var time = value.getTime();
18090         if(this.minValue && time < this.minValue.getTime()){
18091             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18092             return false;
18093         }
18094         if(this.maxValue && time > this.maxValue.getTime()){
18095             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18096             return false;
18097         }
18098         /*if(this.disabledDays){
18099             var day = value.getDay();
18100             for(var i = 0; i < this.disabledDays.length; i++) {
18101                 if(day === this.disabledDays[i]){
18102                     this.markInvalid(this.disabledDaysText);
18103                     return false;
18104                 }
18105             }
18106         }
18107         */
18108         var fvalue = this.formatDate(value);
18109         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18110             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18111             return false;
18112         }
18113         */
18114         return true;
18115     },
18116
18117     // private
18118     // Provides logic to override the default TriggerField.validateBlur which just returns true
18119     validateBlur : function(){
18120         return !this.menu || !this.menu.isVisible();
18121     },
18122
18123     /**
18124      * Returns the current date value of the date field.
18125      * @return {Date} The date value
18126      */
18127     getValue : function(){
18128         
18129         
18130         
18131         return  this.hiddenField ?
18132                 this.hiddenField.value :
18133                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18134     },
18135
18136     /**
18137      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18138      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18139      * (the default format used is "m/d/y").
18140      * <br />Usage:
18141      * <pre><code>
18142 //All of these calls set the same date value (May 4, 2006)
18143
18144 //Pass a date object:
18145 var dt = new Date('5/4/06');
18146 monthField.setValue(dt);
18147
18148 //Pass a date string (default format):
18149 monthField.setValue('5/4/06');
18150
18151 //Pass a date string (custom format):
18152 monthField.format = 'Y-m-d';
18153 monthField.setValue('2006-5-4');
18154 </code></pre>
18155      * @param {String/Date} date The date or valid date string
18156      */
18157     setValue : function(date){
18158         Roo.log('month setValue' + date);
18159         // can only be first of month..
18160         
18161         var val = this.parseDate(date);
18162         
18163         if (this.hiddenField) {
18164             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18165         }
18166         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18167         this.value = this.parseDate(date);
18168     },
18169
18170     // private
18171     parseDate : function(value){
18172         if(!value || value instanceof Date){
18173             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18174             return value;
18175         }
18176         var v = Date.parseDate(value, this.format);
18177         if (!v && this.useIso) {
18178             v = Date.parseDate(value, 'Y-m-d');
18179         }
18180         if (v) {
18181             // 
18182             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18183         }
18184         
18185         
18186         if(!v && this.altFormats){
18187             if(!this.altFormatsArray){
18188                 this.altFormatsArray = this.altFormats.split("|");
18189             }
18190             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18191                 v = Date.parseDate(value, this.altFormatsArray[i]);
18192             }
18193         }
18194         return v;
18195     },
18196
18197     // private
18198     formatDate : function(date, fmt){
18199         return (!date || !(date instanceof Date)) ?
18200                date : date.dateFormat(fmt || this.format);
18201     },
18202
18203     // private
18204     menuListeners : {
18205         select: function(m, d){
18206             this.setValue(d);
18207             this.fireEvent('select', this, d);
18208         },
18209         show : function(){ // retain focus styling
18210             this.onFocus();
18211         },
18212         hide : function(){
18213             this.focus.defer(10, this);
18214             var ml = this.menuListeners;
18215             this.menu.un("select", ml.select,  this);
18216             this.menu.un("show", ml.show,  this);
18217             this.menu.un("hide", ml.hide,  this);
18218         }
18219     },
18220     // private
18221     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18222     onTriggerClick : function(){
18223         if(this.disabled){
18224             return;
18225         }
18226         if(this.menu == null){
18227             this.menu = new Roo.menu.DateMenu();
18228            
18229         }
18230         
18231         Roo.apply(this.menu.picker,  {
18232             
18233             showClear: this.allowBlank,
18234             minDate : this.minValue,
18235             maxDate : this.maxValue,
18236             disabledDatesRE : this.ddMatch,
18237             disabledDatesText : this.disabledDatesText,
18238             
18239             format : this.useIso ? 'Y-m-d' : this.format,
18240             minText : String.format(this.minText, this.formatDate(this.minValue)),
18241             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18242             
18243         });
18244          this.menu.on(Roo.apply({}, this.menuListeners, {
18245             scope:this
18246         }));
18247        
18248         
18249         var m = this.menu;
18250         var p = m.picker;
18251         
18252         // hide month picker get's called when we called by 'before hide';
18253         
18254         var ignorehide = true;
18255         p.hideMonthPicker  = function(disableAnim){
18256             if (ignorehide) {
18257                 return;
18258             }
18259              if(this.monthPicker){
18260                 Roo.log("hideMonthPicker called");
18261                 if(disableAnim === true){
18262                     this.monthPicker.hide();
18263                 }else{
18264                     this.monthPicker.slideOut('t', {duration:.2});
18265                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18266                     p.fireEvent("select", this, this.value);
18267                     m.hide();
18268                 }
18269             }
18270         }
18271         
18272         Roo.log('picker set value');
18273         Roo.log(this.getValue());
18274         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18275         m.show(this.el, 'tl-bl?');
18276         ignorehide  = false;
18277         // this will trigger hideMonthPicker..
18278         
18279         
18280         // hidden the day picker
18281         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18282         
18283         
18284         
18285       
18286         
18287         p.showMonthPicker.defer(100, p);
18288     
18289         
18290        
18291     },
18292
18293     beforeBlur : function(){
18294         var v = this.parseDate(this.getRawValue());
18295         if(v){
18296             this.setValue(v);
18297         }
18298     }
18299
18300     /** @cfg {Boolean} grow @hide */
18301     /** @cfg {Number} growMin @hide */
18302     /** @cfg {Number} growMax @hide */
18303     /**
18304      * @hide
18305      * @method autoSize
18306      */
18307 });/*
18308  * Based on:
18309  * Ext JS Library 1.1.1
18310  * Copyright(c) 2006-2007, Ext JS, LLC.
18311  *
18312  * Originally Released Under LGPL - original licence link has changed is not relivant.
18313  *
18314  * Fork - LGPL
18315  * <script type="text/javascript">
18316  */
18317  
18318
18319 /**
18320  * @class Roo.form.ComboBox
18321  * @extends Roo.form.TriggerField
18322  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18323  * @constructor
18324  * Create a new ComboBox.
18325  * @param {Object} config Configuration options
18326  */
18327 Roo.form.ComboBox = function(config){
18328     Roo.form.ComboBox.superclass.constructor.call(this, config);
18329     this.addEvents({
18330         /**
18331          * @event expand
18332          * Fires when the dropdown list is expanded
18333              * @param {Roo.form.ComboBox} combo This combo box
18334              */
18335         'expand' : true,
18336         /**
18337          * @event collapse
18338          * Fires when the dropdown list is collapsed
18339              * @param {Roo.form.ComboBox} combo This combo box
18340              */
18341         'collapse' : true,
18342         /**
18343          * @event beforeselect
18344          * Fires before a list item is selected. Return false to cancel the selection.
18345              * @param {Roo.form.ComboBox} combo This combo box
18346              * @param {Roo.data.Record} record The data record returned from the underlying store
18347              * @param {Number} index The index of the selected item in the dropdown list
18348              */
18349         'beforeselect' : true,
18350         /**
18351          * @event select
18352          * Fires when a list item is selected
18353              * @param {Roo.form.ComboBox} combo This combo box
18354              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18355              * @param {Number} index The index of the selected item in the dropdown list
18356              */
18357         'select' : true,
18358         /**
18359          * @event beforequery
18360          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18361          * The event object passed has these properties:
18362              * @param {Roo.form.ComboBox} combo This combo box
18363              * @param {String} query The query
18364              * @param {Boolean} forceAll true to force "all" query
18365              * @param {Boolean} cancel true to cancel the query
18366              * @param {Object} e The query event object
18367              */
18368         'beforequery': true,
18369          /**
18370          * @event add
18371          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18372              * @param {Roo.form.ComboBox} combo This combo box
18373              */
18374         'add' : true,
18375         /**
18376          * @event edit
18377          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18378              * @param {Roo.form.ComboBox} combo This combo box
18379              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18380              */
18381         'edit' : true
18382         
18383         
18384     });
18385     if(this.transform){
18386         this.allowDomMove = false;
18387         var s = Roo.getDom(this.transform);
18388         if(!this.hiddenName){
18389             this.hiddenName = s.name;
18390         }
18391         if(!this.store){
18392             this.mode = 'local';
18393             var d = [], opts = s.options;
18394             for(var i = 0, len = opts.length;i < len; i++){
18395                 var o = opts[i];
18396                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18397                 if(o.selected) {
18398                     this.value = value;
18399                 }
18400                 d.push([value, o.text]);
18401             }
18402             this.store = new Roo.data.SimpleStore({
18403                 'id': 0,
18404                 fields: ['value', 'text'],
18405                 data : d
18406             });
18407             this.valueField = 'value';
18408             this.displayField = 'text';
18409         }
18410         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18411         if(!this.lazyRender){
18412             this.target = true;
18413             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18414             s.parentNode.removeChild(s); // remove it
18415             this.render(this.el.parentNode);
18416         }else{
18417             s.parentNode.removeChild(s); // remove it
18418         }
18419
18420     }
18421     if (this.store) {
18422         this.store = Roo.factory(this.store, Roo.data);
18423     }
18424     
18425     this.selectedIndex = -1;
18426     if(this.mode == 'local'){
18427         if(config.queryDelay === undefined){
18428             this.queryDelay = 10;
18429         }
18430         if(config.minChars === undefined){
18431             this.minChars = 0;
18432         }
18433     }
18434 };
18435
18436 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18437     /**
18438      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18439      */
18440     /**
18441      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18442      * rendering into an Roo.Editor, defaults to false)
18443      */
18444     /**
18445      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18446      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18447      */
18448     /**
18449      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18450      */
18451     /**
18452      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18453      * the dropdown list (defaults to undefined, with no header element)
18454      */
18455
18456      /**
18457      * @cfg {String/Roo.Template} tpl The template to use to render the output
18458      */
18459      
18460     // private
18461     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18462     /**
18463      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18464      */
18465     listWidth: undefined,
18466     /**
18467      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18468      * mode = 'remote' or 'text' if mode = 'local')
18469      */
18470     displayField: undefined,
18471     /**
18472      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18473      * mode = 'remote' or 'value' if mode = 'local'). 
18474      * Note: use of a valueField requires the user make a selection
18475      * in order for a value to be mapped.
18476      */
18477     valueField: undefined,
18478     
18479     
18480     /**
18481      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18482      * field's data value (defaults to the underlying DOM element's name)
18483      */
18484     hiddenName: undefined,
18485     /**
18486      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18487      */
18488     listClass: '',
18489     /**
18490      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18491      */
18492     selectedClass: 'x-combo-selected',
18493     /**
18494      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18495      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18496      * which displays a downward arrow icon).
18497      */
18498     triggerClass : 'x-form-arrow-trigger',
18499     /**
18500      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18501      */
18502     shadow:'sides',
18503     /**
18504      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18505      * anchor positions (defaults to 'tl-bl')
18506      */
18507     listAlign: 'tl-bl?',
18508     /**
18509      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18510      */
18511     maxHeight: 300,
18512     /**
18513      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18514      * query specified by the allQuery config option (defaults to 'query')
18515      */
18516     triggerAction: 'query',
18517     /**
18518      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18519      * (defaults to 4, does not apply if editable = false)
18520      */
18521     minChars : 4,
18522     /**
18523      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18524      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18525      */
18526     typeAhead: false,
18527     /**
18528      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18529      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18530      */
18531     queryDelay: 500,
18532     /**
18533      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18534      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18535      */
18536     pageSize: 0,
18537     /**
18538      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18539      * when editable = true (defaults to false)
18540      */
18541     selectOnFocus:false,
18542     /**
18543      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18544      */
18545     queryParam: 'query',
18546     /**
18547      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18548      * when mode = 'remote' (defaults to 'Loading...')
18549      */
18550     loadingText: 'Loading...',
18551     /**
18552      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18553      */
18554     resizable: false,
18555     /**
18556      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18557      */
18558     handleHeight : 8,
18559     /**
18560      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18561      * traditional select (defaults to true)
18562      */
18563     editable: true,
18564     /**
18565      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18566      */
18567     allQuery: '',
18568     /**
18569      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18570      */
18571     mode: 'remote',
18572     /**
18573      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18574      * listWidth has a higher value)
18575      */
18576     minListWidth : 70,
18577     /**
18578      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18579      * allow the user to set arbitrary text into the field (defaults to false)
18580      */
18581     forceSelection:false,
18582     /**
18583      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18584      * if typeAhead = true (defaults to 250)
18585      */
18586     typeAheadDelay : 250,
18587     /**
18588      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18589      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18590      */
18591     valueNotFoundText : undefined,
18592     /**
18593      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18594      */
18595     blockFocus : false,
18596     
18597     /**
18598      * @cfg {Boolean} disableClear Disable showing of clear button.
18599      */
18600     disableClear : false,
18601     /**
18602      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18603      */
18604     alwaysQuery : false,
18605     
18606     //private
18607     addicon : false,
18608     editicon: false,
18609     
18610     // element that contains real text value.. (when hidden is used..)
18611      
18612     // private
18613     onRender : function(ct, position)
18614     {
18615         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18616         
18617         if(this.hiddenName){
18618             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18619                     'before', true);
18620             this.hiddenField.value =
18621                 this.hiddenValue !== undefined ? this.hiddenValue :
18622                 this.value !== undefined ? this.value : '';
18623
18624             // prevent input submission
18625             this.el.dom.removeAttribute('name');
18626              
18627              
18628         }
18629         
18630         if(Roo.isGecko){
18631             this.el.dom.setAttribute('autocomplete', 'off');
18632         }
18633
18634         var cls = 'x-combo-list';
18635
18636         this.list = new Roo.Layer({
18637             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18638         });
18639
18640         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18641         this.list.setWidth(lw);
18642         this.list.swallowEvent('mousewheel');
18643         this.assetHeight = 0;
18644
18645         if(this.title){
18646             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18647             this.assetHeight += this.header.getHeight();
18648         }
18649
18650         this.innerList = this.list.createChild({cls:cls+'-inner'});
18651         this.innerList.on('mouseover', this.onViewOver, this);
18652         this.innerList.on('mousemove', this.onViewMove, this);
18653         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18654         
18655         if(this.allowBlank && !this.pageSize && !this.disableClear){
18656             this.footer = this.list.createChild({cls:cls+'-ft'});
18657             this.pageTb = new Roo.Toolbar(this.footer);
18658            
18659         }
18660         if(this.pageSize){
18661             this.footer = this.list.createChild({cls:cls+'-ft'});
18662             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18663                     {pageSize: this.pageSize});
18664             
18665         }
18666         
18667         if (this.pageTb && this.allowBlank && !this.disableClear) {
18668             var _this = this;
18669             this.pageTb.add(new Roo.Toolbar.Fill(), {
18670                 cls: 'x-btn-icon x-btn-clear',
18671                 text: '&#160;',
18672                 handler: function()
18673                 {
18674                     _this.collapse();
18675                     _this.clearValue();
18676                     _this.onSelect(false, -1);
18677                 }
18678             });
18679         }
18680         if (this.footer) {
18681             this.assetHeight += this.footer.getHeight();
18682         }
18683         
18684
18685         if(!this.tpl){
18686             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18687         }
18688
18689         this.view = new Roo.View(this.innerList, this.tpl, {
18690             singleSelect:true,
18691             store: this.store,
18692             selectedClass: this.selectedClass
18693         });
18694
18695         this.view.on('click', this.onViewClick, this);
18696
18697         this.store.on('beforeload', this.onBeforeLoad, this);
18698         this.store.on('load', this.onLoad, this);
18699         this.store.on('loadexception', this.onLoadException, this);
18700
18701         if(this.resizable){
18702             this.resizer = new Roo.Resizable(this.list,  {
18703                pinned:true, handles:'se'
18704             });
18705             this.resizer.on('resize', function(r, w, h){
18706                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18707                 this.listWidth = w;
18708                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18709                 this.restrictHeight();
18710             }, this);
18711             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18712         }
18713         if(!this.editable){
18714             this.editable = true;
18715             this.setEditable(false);
18716         }  
18717         
18718         
18719         if (typeof(this.events.add.listeners) != 'undefined') {
18720             
18721             this.addicon = this.wrap.createChild(
18722                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18723        
18724             this.addicon.on('click', function(e) {
18725                 this.fireEvent('add', this);
18726             }, this);
18727         }
18728         if (typeof(this.events.edit.listeners) != 'undefined') {
18729             
18730             this.editicon = this.wrap.createChild(
18731                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18732             if (this.addicon) {
18733                 this.editicon.setStyle('margin-left', '40px');
18734             }
18735             this.editicon.on('click', function(e) {
18736                 
18737                 // we fire even  if inothing is selected..
18738                 this.fireEvent('edit', this, this.lastData );
18739                 
18740             }, this);
18741         }
18742         
18743         
18744         
18745     },
18746
18747     // private
18748     initEvents : function(){
18749         Roo.form.ComboBox.superclass.initEvents.call(this);
18750
18751         this.keyNav = new Roo.KeyNav(this.el, {
18752             "up" : function(e){
18753                 this.inKeyMode = true;
18754                 this.selectPrev();
18755             },
18756
18757             "down" : function(e){
18758                 if(!this.isExpanded()){
18759                     this.onTriggerClick();
18760                 }else{
18761                     this.inKeyMode = true;
18762                     this.selectNext();
18763                 }
18764             },
18765
18766             "enter" : function(e){
18767                 this.onViewClick();
18768                 //return true;
18769             },
18770
18771             "esc" : function(e){
18772                 this.collapse();
18773             },
18774
18775             "tab" : function(e){
18776                 this.onViewClick(false);
18777                 this.fireEvent("specialkey", this, e);
18778                 return true;
18779             },
18780
18781             scope : this,
18782
18783             doRelay : function(foo, bar, hname){
18784                 if(hname == 'down' || this.scope.isExpanded()){
18785                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18786                 }
18787                 return true;
18788             },
18789
18790             forceKeyDown: true
18791         });
18792         this.queryDelay = Math.max(this.queryDelay || 10,
18793                 this.mode == 'local' ? 10 : 250);
18794         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18795         if(this.typeAhead){
18796             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18797         }
18798         if(this.editable !== false){
18799             this.el.on("keyup", this.onKeyUp, this);
18800         }
18801         if(this.forceSelection){
18802             this.on('blur', this.doForce, this);
18803         }
18804     },
18805
18806     onDestroy : function(){
18807         if(this.view){
18808             this.view.setStore(null);
18809             this.view.el.removeAllListeners();
18810             this.view.el.remove();
18811             this.view.purgeListeners();
18812         }
18813         if(this.list){
18814             this.list.destroy();
18815         }
18816         if(this.store){
18817             this.store.un('beforeload', this.onBeforeLoad, this);
18818             this.store.un('load', this.onLoad, this);
18819             this.store.un('loadexception', this.onLoadException, this);
18820         }
18821         Roo.form.ComboBox.superclass.onDestroy.call(this);
18822     },
18823
18824     // private
18825     fireKey : function(e){
18826         if(e.isNavKeyPress() && !this.list.isVisible()){
18827             this.fireEvent("specialkey", this, e);
18828         }
18829     },
18830
18831     // private
18832     onResize: function(w, h){
18833         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18834         
18835         if(typeof w != 'number'){
18836             // we do not handle it!?!?
18837             return;
18838         }
18839         var tw = this.trigger.getWidth();
18840         tw += this.addicon ? this.addicon.getWidth() : 0;
18841         tw += this.editicon ? this.editicon.getWidth() : 0;
18842         var x = w - tw;
18843         this.el.setWidth( this.adjustWidth('input', x));
18844             
18845         this.trigger.setStyle('left', x+'px');
18846         
18847         if(this.list && this.listWidth === undefined){
18848             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18849             this.list.setWidth(lw);
18850             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18851         }
18852         
18853     
18854         
18855     },
18856
18857     /**
18858      * Allow or prevent the user from directly editing the field text.  If false is passed,
18859      * the user will only be able to select from the items defined in the dropdown list.  This method
18860      * is the runtime equivalent of setting the 'editable' config option at config time.
18861      * @param {Boolean} value True to allow the user to directly edit the field text
18862      */
18863     setEditable : function(value){
18864         if(value == this.editable){
18865             return;
18866         }
18867         this.editable = value;
18868         if(!value){
18869             this.el.dom.setAttribute('readOnly', true);
18870             this.el.on('mousedown', this.onTriggerClick,  this);
18871             this.el.addClass('x-combo-noedit');
18872         }else{
18873             this.el.dom.setAttribute('readOnly', false);
18874             this.el.un('mousedown', this.onTriggerClick,  this);
18875             this.el.removeClass('x-combo-noedit');
18876         }
18877     },
18878
18879     // private
18880     onBeforeLoad : function(){
18881         if(!this.hasFocus){
18882             return;
18883         }
18884         this.innerList.update(this.loadingText ?
18885                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18886         this.restrictHeight();
18887         this.selectedIndex = -1;
18888     },
18889
18890     // private
18891     onLoad : function(){
18892         if(!this.hasFocus){
18893             return;
18894         }
18895         if(this.store.getCount() > 0){
18896             this.expand();
18897             this.restrictHeight();
18898             if(this.lastQuery == this.allQuery){
18899                 if(this.editable){
18900                     this.el.dom.select();
18901                 }
18902                 if(!this.selectByValue(this.value, true)){
18903                     this.select(0, true);
18904                 }
18905             }else{
18906                 this.selectNext();
18907                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18908                     this.taTask.delay(this.typeAheadDelay);
18909                 }
18910             }
18911         }else{
18912             this.onEmptyResults();
18913         }
18914         //this.el.focus();
18915     },
18916     // private
18917     onLoadException : function()
18918     {
18919         this.collapse();
18920         Roo.log(this.store.reader.jsonData);
18921         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18922             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18923         }
18924         
18925         
18926     },
18927     // private
18928     onTypeAhead : function(){
18929         if(this.store.getCount() > 0){
18930             var r = this.store.getAt(0);
18931             var newValue = r.data[this.displayField];
18932             var len = newValue.length;
18933             var selStart = this.getRawValue().length;
18934             if(selStart != len){
18935                 this.setRawValue(newValue);
18936                 this.selectText(selStart, newValue.length);
18937             }
18938         }
18939     },
18940
18941     // private
18942     onSelect : function(record, index){
18943         if(this.fireEvent('beforeselect', this, record, index) !== false){
18944             this.setFromData(index > -1 ? record.data : false);
18945             this.collapse();
18946             this.fireEvent('select', this, record, index);
18947         }
18948     },
18949
18950     /**
18951      * Returns the currently selected field value or empty string if no value is set.
18952      * @return {String} value The selected value
18953      */
18954     getValue : function(){
18955         if(this.valueField){
18956             return typeof this.value != 'undefined' ? this.value : '';
18957         }
18958         return Roo.form.ComboBox.superclass.getValue.call(this);
18959     },
18960
18961     /**
18962      * Clears any text/value currently set in the field
18963      */
18964     clearValue : function(){
18965         if(this.hiddenField){
18966             this.hiddenField.value = '';
18967         }
18968         this.value = '';
18969         this.setRawValue('');
18970         this.lastSelectionText = '';
18971         
18972     },
18973
18974     /**
18975      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18976      * will be displayed in the field.  If the value does not match the data value of an existing item,
18977      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18978      * Otherwise the field will be blank (although the value will still be set).
18979      * @param {String} value The value to match
18980      */
18981     setValue : function(v){
18982         var text = v;
18983         if(this.valueField){
18984             var r = this.findRecord(this.valueField, v);
18985             if(r){
18986                 text = r.data[this.displayField];
18987             }else if(this.valueNotFoundText !== undefined){
18988                 text = this.valueNotFoundText;
18989             }
18990         }
18991         this.lastSelectionText = text;
18992         if(this.hiddenField){
18993             this.hiddenField.value = v;
18994         }
18995         Roo.form.ComboBox.superclass.setValue.call(this, text);
18996         this.value = v;
18997     },
18998     /**
18999      * @property {Object} the last set data for the element
19000      */
19001     
19002     lastData : false,
19003     /**
19004      * Sets the value of the field based on a object which is related to the record format for the store.
19005      * @param {Object} value the value to set as. or false on reset?
19006      */
19007     setFromData : function(o){
19008         var dv = ''; // display value
19009         var vv = ''; // value value..
19010         this.lastData = o;
19011         if (this.displayField) {
19012             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19013         } else {
19014             // this is an error condition!!!
19015             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19016         }
19017         
19018         if(this.valueField){
19019             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19020         }
19021         if(this.hiddenField){
19022             this.hiddenField.value = vv;
19023             
19024             this.lastSelectionText = dv;
19025             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19026             this.value = vv;
19027             return;
19028         }
19029         // no hidden field.. - we store the value in 'value', but still display
19030         // display field!!!!
19031         this.lastSelectionText = dv;
19032         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19033         this.value = vv;
19034         
19035         
19036     },
19037     // private
19038     reset : function(){
19039         // overridden so that last data is reset..
19040         this.setValue(this.resetValue);
19041         this.originalValue = this.getValue();
19042         this.clearInvalid();
19043         this.lastData = false;
19044         if (this.view) {
19045             this.view.clearSelections();
19046         }
19047     },
19048     // private
19049     findRecord : function(prop, value){
19050         var record;
19051         if(this.store.getCount() > 0){
19052             this.store.each(function(r){
19053                 if(r.data[prop] == value){
19054                     record = r;
19055                     return false;
19056                 }
19057                 return true;
19058             });
19059         }
19060         return record;
19061     },
19062     
19063     getName: function()
19064     {
19065         // returns hidden if it's set..
19066         if (!this.rendered) {return ''};
19067         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19068         
19069     },
19070     // private
19071     onViewMove : function(e, t){
19072         this.inKeyMode = false;
19073     },
19074
19075     // private
19076     onViewOver : function(e, t){
19077         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19078             return;
19079         }
19080         var item = this.view.findItemFromChild(t);
19081         if(item){
19082             var index = this.view.indexOf(item);
19083             this.select(index, false);
19084         }
19085     },
19086
19087     // private
19088     onViewClick : function(doFocus)
19089     {
19090         var index = this.view.getSelectedIndexes()[0];
19091         var r = this.store.getAt(index);
19092         if(r){
19093             this.onSelect(r, index);
19094         }
19095         if(doFocus !== false && !this.blockFocus){
19096             this.el.focus();
19097         }
19098     },
19099
19100     // private
19101     restrictHeight : function(){
19102         this.innerList.dom.style.height = '';
19103         var inner = this.innerList.dom;
19104         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19105         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19106         this.list.beginUpdate();
19107         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19108         this.list.alignTo(this.el, this.listAlign);
19109         this.list.endUpdate();
19110     },
19111
19112     // private
19113     onEmptyResults : function(){
19114         this.collapse();
19115     },
19116
19117     /**
19118      * Returns true if the dropdown list is expanded, else false.
19119      */
19120     isExpanded : function(){
19121         return this.list.isVisible();
19122     },
19123
19124     /**
19125      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19126      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19127      * @param {String} value The data value of the item to select
19128      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19129      * selected item if it is not currently in view (defaults to true)
19130      * @return {Boolean} True if the value matched an item in the list, else false
19131      */
19132     selectByValue : function(v, scrollIntoView){
19133         if(v !== undefined && v !== null){
19134             var r = this.findRecord(this.valueField || this.displayField, v);
19135             if(r){
19136                 this.select(this.store.indexOf(r), scrollIntoView);
19137                 return true;
19138             }
19139         }
19140         return false;
19141     },
19142
19143     /**
19144      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19145      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19146      * @param {Number} index The zero-based index of the list item to select
19147      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19148      * selected item if it is not currently in view (defaults to true)
19149      */
19150     select : function(index, scrollIntoView){
19151         this.selectedIndex = index;
19152         this.view.select(index);
19153         if(scrollIntoView !== false){
19154             var el = this.view.getNode(index);
19155             if(el){
19156                 this.innerList.scrollChildIntoView(el, false);
19157             }
19158         }
19159     },
19160
19161     // private
19162     selectNext : function(){
19163         var ct = this.store.getCount();
19164         if(ct > 0){
19165             if(this.selectedIndex == -1){
19166                 this.select(0);
19167             }else if(this.selectedIndex < ct-1){
19168                 this.select(this.selectedIndex+1);
19169             }
19170         }
19171     },
19172
19173     // private
19174     selectPrev : function(){
19175         var ct = this.store.getCount();
19176         if(ct > 0){
19177             if(this.selectedIndex == -1){
19178                 this.select(0);
19179             }else if(this.selectedIndex != 0){
19180                 this.select(this.selectedIndex-1);
19181             }
19182         }
19183     },
19184
19185     // private
19186     onKeyUp : function(e){
19187         if(this.editable !== false && !e.isSpecialKey()){
19188             this.lastKey = e.getKey();
19189             this.dqTask.delay(this.queryDelay);
19190         }
19191     },
19192
19193     // private
19194     validateBlur : function(){
19195         return !this.list || !this.list.isVisible();   
19196     },
19197
19198     // private
19199     initQuery : function(){
19200         this.doQuery(this.getRawValue());
19201     },
19202
19203     // private
19204     doForce : function(){
19205         if(this.el.dom.value.length > 0){
19206             this.el.dom.value =
19207                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19208              
19209         }
19210     },
19211
19212     /**
19213      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19214      * query allowing the query action to be canceled if needed.
19215      * @param {String} query The SQL query to execute
19216      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19217      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19218      * saved in the current store (defaults to false)
19219      */
19220     doQuery : function(q, forceAll){
19221         if(q === undefined || q === null){
19222             q = '';
19223         }
19224         var qe = {
19225             query: q,
19226             forceAll: forceAll,
19227             combo: this,
19228             cancel:false
19229         };
19230         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19231             return false;
19232         }
19233         q = qe.query;
19234         forceAll = qe.forceAll;
19235         if(forceAll === true || (q.length >= this.minChars)){
19236             if(this.lastQuery != q || this.alwaysQuery){
19237                 this.lastQuery = q;
19238                 if(this.mode == 'local'){
19239                     this.selectedIndex = -1;
19240                     if(forceAll){
19241                         this.store.clearFilter();
19242                     }else{
19243                         this.store.filter(this.displayField, q);
19244                     }
19245                     this.onLoad();
19246                 }else{
19247                     this.store.baseParams[this.queryParam] = q;
19248                     this.store.load({
19249                         params: this.getParams(q)
19250                     });
19251                     this.expand();
19252                 }
19253             }else{
19254                 this.selectedIndex = -1;
19255                 this.onLoad();   
19256             }
19257         }
19258     },
19259
19260     // private
19261     getParams : function(q){
19262         var p = {};
19263         //p[this.queryParam] = q;
19264         if(this.pageSize){
19265             p.start = 0;
19266             p.limit = this.pageSize;
19267         }
19268         return p;
19269     },
19270
19271     /**
19272      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19273      */
19274     collapse : function(){
19275         if(!this.isExpanded()){
19276             return;
19277         }
19278         this.list.hide();
19279         Roo.get(document).un('mousedown', this.collapseIf, this);
19280         Roo.get(document).un('mousewheel', this.collapseIf, this);
19281         if (!this.editable) {
19282             Roo.get(document).un('keydown', this.listKeyPress, this);
19283         }
19284         this.fireEvent('collapse', this);
19285     },
19286
19287     // private
19288     collapseIf : function(e){
19289         if(!e.within(this.wrap) && !e.within(this.list)){
19290             this.collapse();
19291         }
19292     },
19293
19294     /**
19295      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19296      */
19297     expand : function(){
19298         if(this.isExpanded() || !this.hasFocus){
19299             return;
19300         }
19301         this.list.alignTo(this.el, this.listAlign);
19302         this.list.show();
19303         Roo.get(document).on('mousedown', this.collapseIf, this);
19304         Roo.get(document).on('mousewheel', this.collapseIf, this);
19305         if (!this.editable) {
19306             Roo.get(document).on('keydown', this.listKeyPress, this);
19307         }
19308         
19309         this.fireEvent('expand', this);
19310     },
19311
19312     // private
19313     // Implements the default empty TriggerField.onTriggerClick function
19314     onTriggerClick : function(){
19315         if(this.disabled){
19316             return;
19317         }
19318         if(this.isExpanded()){
19319             this.collapse();
19320             if (!this.blockFocus) {
19321                 this.el.focus();
19322             }
19323             
19324         }else {
19325             this.hasFocus = true;
19326             if(this.triggerAction == 'all') {
19327                 this.doQuery(this.allQuery, true);
19328             } else {
19329                 this.doQuery(this.getRawValue());
19330             }
19331             if (!this.blockFocus) {
19332                 this.el.focus();
19333             }
19334         }
19335     },
19336     listKeyPress : function(e)
19337     {
19338         //Roo.log('listkeypress');
19339         // scroll to first matching element based on key pres..
19340         if (e.isSpecialKey()) {
19341             return false;
19342         }
19343         var k = String.fromCharCode(e.getKey()).toUpperCase();
19344         //Roo.log(k);
19345         var match  = false;
19346         var csel = this.view.getSelectedNodes();
19347         var cselitem = false;
19348         if (csel.length) {
19349             var ix = this.view.indexOf(csel[0]);
19350             cselitem  = this.store.getAt(ix);
19351             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19352                 cselitem = false;
19353             }
19354             
19355         }
19356         
19357         this.store.each(function(v) { 
19358             if (cselitem) {
19359                 // start at existing selection.
19360                 if (cselitem.id == v.id) {
19361                     cselitem = false;
19362                 }
19363                 return;
19364             }
19365                 
19366             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19367                 match = this.store.indexOf(v);
19368                 return false;
19369             }
19370         }, this);
19371         
19372         if (match === false) {
19373             return true; // no more action?
19374         }
19375         // scroll to?
19376         this.view.select(match);
19377         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19378         sn.scrollIntoView(sn.dom.parentNode, false);
19379     } 
19380
19381     /** 
19382     * @cfg {Boolean} grow 
19383     * @hide 
19384     */
19385     /** 
19386     * @cfg {Number} growMin 
19387     * @hide 
19388     */
19389     /** 
19390     * @cfg {Number} growMax 
19391     * @hide 
19392     */
19393     /**
19394      * @hide
19395      * @method autoSize
19396      */
19397 });/*
19398  * Copyright(c) 2010-2012, Roo J Solutions Limited
19399  *
19400  * Licence LGPL
19401  *
19402  */
19403
19404 /**
19405  * @class Roo.form.ComboBoxArray
19406  * @extends Roo.form.TextField
19407  * A facebook style adder... for lists of email / people / countries  etc...
19408  * pick multiple items from a combo box, and shows each one.
19409  *
19410  *  Fred [x]  Brian [x]  [Pick another |v]
19411  *
19412  *
19413  *  For this to work: it needs various extra information
19414  *    - normal combo problay has
19415  *      name, hiddenName
19416  *    + displayField, valueField
19417  *
19418  *    For our purpose...
19419  *
19420  *
19421  *   If we change from 'extends' to wrapping...
19422  *   
19423  *  
19424  *
19425  
19426  
19427  * @constructor
19428  * Create a new ComboBoxArray.
19429  * @param {Object} config Configuration options
19430  */
19431  
19432
19433 Roo.form.ComboBoxArray = function(config)
19434 {
19435     this.addEvents({
19436         /**
19437          * @event beforeremove
19438          * Fires before remove the value from the list
19439              * @param {Roo.form.ComboBoxArray} _self This combo box array
19440              * @param {Roo.form.ComboBoxArray.Item} item removed item
19441              */
19442         'beforeremove' : true,
19443         /**
19444          * @event remove
19445          * Fires when remove the value from the list
19446              * @param {Roo.form.ComboBoxArray} _self This combo box array
19447              * @param {Roo.form.ComboBoxArray.Item} item removed item
19448              */
19449         'remove' : true
19450         
19451         
19452     });
19453     
19454     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19455     
19456     this.items = new Roo.util.MixedCollection(false);
19457     
19458     // construct the child combo...
19459     
19460     
19461     
19462     
19463    
19464     
19465 }
19466
19467  
19468 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19469
19470     /**
19471      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19472      */
19473     
19474     lastData : false,
19475     
19476     // behavies liek a hiddne field
19477     inputType:      'hidden',
19478     /**
19479      * @cfg {Number} width The width of the box that displays the selected element
19480      */ 
19481     width:          300,
19482
19483     
19484     
19485     /**
19486      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19487      */
19488     name : false,
19489     /**
19490      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19491      */
19492     hiddenName : false,
19493       /**
19494      * @cfg {String} seperator    The value seperator normally ',' 
19495      */
19496     seperator : ',',
19497     
19498     // private the array of items that are displayed..
19499     items  : false,
19500     // private - the hidden field el.
19501     hiddenEl : false,
19502     // private - the filed el..
19503     el : false,
19504     
19505     //validateValue : function() { return true; }, // all values are ok!
19506     //onAddClick: function() { },
19507     
19508     onRender : function(ct, position) 
19509     {
19510         
19511         // create the standard hidden element
19512         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19513         
19514         
19515         // give fake names to child combo;
19516         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19517         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19518         
19519         this.combo = Roo.factory(this.combo, Roo.form);
19520         this.combo.onRender(ct, position);
19521         if (typeof(this.combo.width) != 'undefined') {
19522             this.combo.onResize(this.combo.width,0);
19523         }
19524         
19525         this.combo.initEvents();
19526         
19527         // assigned so form know we need to do this..
19528         this.store          = this.combo.store;
19529         this.valueField     = this.combo.valueField;
19530         this.displayField   = this.combo.displayField ;
19531         
19532         
19533         this.combo.wrap.addClass('x-cbarray-grp');
19534         
19535         var cbwrap = this.combo.wrap.createChild(
19536             {tag: 'div', cls: 'x-cbarray-cb'},
19537             this.combo.el.dom
19538         );
19539         
19540              
19541         this.hiddenEl = this.combo.wrap.createChild({
19542             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19543         });
19544         this.el = this.combo.wrap.createChild({
19545             tag: 'input',  type:'hidden' , name: this.name, value : ''
19546         });
19547          //   this.el.dom.removeAttribute("name");
19548         
19549         
19550         this.outerWrap = this.combo.wrap;
19551         this.wrap = cbwrap;
19552         
19553         this.outerWrap.setWidth(this.width);
19554         this.outerWrap.dom.removeChild(this.el.dom);
19555         
19556         this.wrap.dom.appendChild(this.el.dom);
19557         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19558         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19559         
19560         this.combo.trigger.setStyle('position','relative');
19561         this.combo.trigger.setStyle('left', '0px');
19562         this.combo.trigger.setStyle('top', '2px');
19563         
19564         this.combo.el.setStyle('vertical-align', 'text-bottom');
19565         
19566         //this.trigger.setStyle('vertical-align', 'top');
19567         
19568         // this should use the code from combo really... on('add' ....)
19569         if (this.adder) {
19570             
19571         
19572             this.adder = this.outerWrap.createChild(
19573                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19574             var _t = this;
19575             this.adder.on('click', function(e) {
19576                 _t.fireEvent('adderclick', this, e);
19577             }, _t);
19578         }
19579         //var _t = this;
19580         //this.adder.on('click', this.onAddClick, _t);
19581         
19582         
19583         this.combo.on('select', function(cb, rec, ix) {
19584             this.addItem(rec.data);
19585             
19586             cb.setValue('');
19587             cb.el.dom.value = '';
19588             //cb.lastData = rec.data;
19589             // add to list
19590             
19591         }, this);
19592         
19593         
19594     },
19595     
19596     
19597     getName: function()
19598     {
19599         // returns hidden if it's set..
19600         if (!this.rendered) {return ''};
19601         return  this.hiddenName ? this.hiddenName : this.name;
19602         
19603     },
19604     
19605     
19606     onResize: function(w, h){
19607         
19608         return;
19609         // not sure if this is needed..
19610         //this.combo.onResize(w,h);
19611         
19612         if(typeof w != 'number'){
19613             // we do not handle it!?!?
19614             return;
19615         }
19616         var tw = this.combo.trigger.getWidth();
19617         tw += this.addicon ? this.addicon.getWidth() : 0;
19618         tw += this.editicon ? this.editicon.getWidth() : 0;
19619         var x = w - tw;
19620         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19621             
19622         this.combo.trigger.setStyle('left', '0px');
19623         
19624         if(this.list && this.listWidth === undefined){
19625             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19626             this.list.setWidth(lw);
19627             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19628         }
19629         
19630     
19631         
19632     },
19633     
19634     addItem: function(rec)
19635     {
19636         var valueField = this.combo.valueField;
19637         var displayField = this.combo.displayField;
19638         
19639         if (this.items.indexOfKey(rec[valueField]) > -1) {
19640             //console.log("GOT " + rec.data.id);
19641             return;
19642         }
19643         
19644         var x = new Roo.form.ComboBoxArray.Item({
19645             //id : rec[this.idField],
19646             data : rec,
19647             displayField : displayField ,
19648             tipField : displayField ,
19649             cb : this
19650         });
19651         // use the 
19652         this.items.add(rec[valueField],x);
19653         // add it before the element..
19654         this.updateHiddenEl();
19655         x.render(this.outerWrap, this.wrap.dom);
19656         // add the image handler..
19657     },
19658     
19659     updateHiddenEl : function()
19660     {
19661         this.validate();
19662         if (!this.hiddenEl) {
19663             return;
19664         }
19665         var ar = [];
19666         var idField = this.combo.valueField;
19667         
19668         this.items.each(function(f) {
19669             ar.push(f.data[idField]);
19670         });
19671         this.hiddenEl.dom.value = ar.join(this.seperator);
19672         this.validate();
19673     },
19674     
19675     reset : function()
19676     {
19677         this.items.clear();
19678         
19679         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19680            el.remove();
19681         });
19682         
19683         this.el.dom.value = '';
19684         if (this.hiddenEl) {
19685             this.hiddenEl.dom.value = '';
19686         }
19687         
19688     },
19689     getValue: function()
19690     {
19691         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19692     },
19693     setValue: function(v) // not a valid action - must use addItems..
19694     {
19695         
19696         this.reset();
19697          
19698         if (this.store.isLocal && (typeof(v) == 'string')) {
19699             // then we can use the store to find the values..
19700             // comma seperated at present.. this needs to allow JSON based encoding..
19701             this.hiddenEl.value  = v;
19702             var v_ar = [];
19703             Roo.each(v.split(this.seperator), function(k) {
19704                 Roo.log("CHECK " + this.valueField + ',' + k);
19705                 var li = this.store.query(this.valueField, k);
19706                 if (!li.length) {
19707                     return;
19708                 }
19709                 var add = {};
19710                 add[this.valueField] = k;
19711                 add[this.displayField] = li.item(0).data[this.displayField];
19712                 
19713                 this.addItem(add);
19714             }, this) 
19715              
19716         }
19717         if (typeof(v) == 'object' ) {
19718             // then let's assume it's an array of objects..
19719             Roo.each(v, function(l) {
19720                 var add = l;
19721                 if (typeof(l) == 'string') {
19722                     add = {};
19723                     add[this.valueField] = l;
19724                     add[this.displayField] = l
19725                 }
19726                 this.addItem(add);
19727             }, this);
19728              
19729         }
19730         
19731         
19732     },
19733     setFromData: function(v)
19734     {
19735         // this recieves an object, if setValues is called.
19736         this.reset();
19737         this.el.dom.value = v[this.displayField];
19738         this.hiddenEl.dom.value = v[this.valueField];
19739         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19740             return;
19741         }
19742         var kv = v[this.valueField];
19743         var dv = v[this.displayField];
19744         kv = typeof(kv) != 'string' ? '' : kv;
19745         dv = typeof(dv) != 'string' ? '' : dv;
19746         
19747         
19748         var keys = kv.split(this.seperator);
19749         var display = dv.split(this.seperator);
19750         for (var i = 0 ; i < keys.length; i++) {
19751             add = {};
19752             add[this.valueField] = keys[i];
19753             add[this.displayField] = display[i];
19754             this.addItem(add);
19755         }
19756       
19757         
19758     },
19759     
19760     /**
19761      * Validates the combox array value
19762      * @return {Boolean} True if the value is valid, else false
19763      */
19764     validate : function(){
19765         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19766             this.clearInvalid();
19767             return true;
19768         }
19769         return false;
19770     },
19771     
19772     validateValue : function(value){
19773         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19774         
19775     },
19776     
19777     /*@
19778      * overide
19779      * 
19780      */
19781     isDirty : function() {
19782         if(this.disabled) {
19783             return false;
19784         }
19785         
19786         try {
19787             var d = Roo.decode(String(this.originalValue));
19788         } catch (e) {
19789             return String(this.getValue()) !== String(this.originalValue);
19790         }
19791         
19792         var originalValue = [];
19793         
19794         for (var i = 0; i < d.length; i++){
19795             originalValue.push(d[i][this.valueField]);
19796         }
19797         
19798         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19799         
19800     }
19801     
19802 });
19803
19804
19805
19806 /**
19807  * @class Roo.form.ComboBoxArray.Item
19808  * @extends Roo.BoxComponent
19809  * A selected item in the list
19810  *  Fred [x]  Brian [x]  [Pick another |v]
19811  * 
19812  * @constructor
19813  * Create a new item.
19814  * @param {Object} config Configuration options
19815  */
19816  
19817 Roo.form.ComboBoxArray.Item = function(config) {
19818     config.id = Roo.id();
19819     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19820 }
19821
19822 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19823     data : {},
19824     cb: false,
19825     displayField : false,
19826     tipField : false,
19827     
19828     
19829     defaultAutoCreate : {
19830         tag: 'div',
19831         cls: 'x-cbarray-item',
19832         cn : [ 
19833             { tag: 'div' },
19834             {
19835                 tag: 'img',
19836                 width:16,
19837                 height : 16,
19838                 src : Roo.BLANK_IMAGE_URL ,
19839                 align: 'center'
19840             }
19841         ]
19842         
19843     },
19844     
19845  
19846     onRender : function(ct, position)
19847     {
19848         Roo.form.Field.superclass.onRender.call(this, ct, position);
19849         
19850         if(!this.el){
19851             var cfg = this.getAutoCreate();
19852             this.el = ct.createChild(cfg, position);
19853         }
19854         
19855         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19856         
19857         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19858             this.cb.renderer(this.data) :
19859             String.format('{0}',this.data[this.displayField]);
19860         
19861             
19862         this.el.child('div').dom.setAttribute('qtip',
19863                         String.format('{0}',this.data[this.tipField])
19864         );
19865         
19866         this.el.child('img').on('click', this.remove, this);
19867         
19868     },
19869    
19870     remove : function()
19871     {
19872         if(this.cb.disabled){
19873             return;
19874         }
19875         
19876         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19877             this.cb.items.remove(this);
19878             this.el.child('img').un('click', this.remove, this);
19879             this.el.remove();
19880             this.cb.updateHiddenEl();
19881
19882             this.cb.fireEvent('remove', this.cb, this);
19883         }
19884         
19885     }
19886 });/*
19887  * RooJS Library 1.1.1
19888  * Copyright(c) 2008-2011  Alan Knowles
19889  *
19890  * License - LGPL
19891  */
19892  
19893
19894 /**
19895  * @class Roo.form.ComboNested
19896  * @extends Roo.form.ComboBox
19897  * A combobox for that allows selection of nested items in a list,
19898  * eg.
19899  *
19900  *  Book
19901  *    -> red
19902  *    -> green
19903  *  Table
19904  *    -> square
19905  *      ->red
19906  *      ->green
19907  *    -> rectangle
19908  *      ->green
19909  *      
19910  * 
19911  * @constructor
19912  * Create a new ComboNested
19913  * @param {Object} config Configuration options
19914  */
19915 Roo.form.ComboNested = function(config){
19916     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19917     // should verify some data...
19918     // like
19919     // hiddenName = required..
19920     // displayField = required
19921     // valudField == required
19922     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19923     var _t = this;
19924     Roo.each(req, function(e) {
19925         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19926             throw "Roo.form.ComboNested : missing value for: " + e;
19927         }
19928     });
19929      
19930     
19931 };
19932
19933 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19934    
19935     /*
19936      * @config {Number} max Number of columns to show
19937      */
19938     
19939     maxColumns : 3,
19940    
19941     list : null, // the outermost div..
19942     innerLists : null, // the
19943     views : null,
19944     stores : null,
19945     // private
19946     loadingChildren : false,
19947     
19948     onRender : function(ct, position)
19949     {
19950         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19951         
19952         if(this.hiddenName){
19953             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19954                     'before', true);
19955             this.hiddenField.value =
19956                 this.hiddenValue !== undefined ? this.hiddenValue :
19957                 this.value !== undefined ? this.value : '';
19958
19959             // prevent input submission
19960             this.el.dom.removeAttribute('name');
19961              
19962              
19963         }
19964         
19965         if(Roo.isGecko){
19966             this.el.dom.setAttribute('autocomplete', 'off');
19967         }
19968
19969         var cls = 'x-combo-list';
19970
19971         this.list = new Roo.Layer({
19972             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19973         });
19974
19975         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19976         this.list.setWidth(lw);
19977         this.list.swallowEvent('mousewheel');
19978         this.assetHeight = 0;
19979
19980         if(this.title){
19981             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19982             this.assetHeight += this.header.getHeight();
19983         }
19984         this.innerLists = [];
19985         this.views = [];
19986         this.stores = [];
19987         for (var i =0 ; i < this.maxColumns; i++) {
19988             this.onRenderList( cls, i);
19989         }
19990         
19991         // always needs footer, as we are going to have an 'OK' button.
19992         this.footer = this.list.createChild({cls:cls+'-ft'});
19993         this.pageTb = new Roo.Toolbar(this.footer);  
19994         var _this = this;
19995         this.pageTb.add(  {
19996             
19997             text: 'Done',
19998             handler: function()
19999             {
20000                 _this.collapse();
20001             }
20002         });
20003         
20004         if ( this.allowBlank && !this.disableClear) {
20005             
20006             this.pageTb.add(new Roo.Toolbar.Fill(), {
20007                 cls: 'x-btn-icon x-btn-clear',
20008                 text: '&#160;',
20009                 handler: function()
20010                 {
20011                     _this.collapse();
20012                     _this.clearValue();
20013                     _this.onSelect(false, -1);
20014                 }
20015             });
20016         }
20017         if (this.footer) {
20018             this.assetHeight += this.footer.getHeight();
20019         }
20020         
20021     },
20022     onRenderList : function (  cls, i)
20023     {
20024         
20025         var lw = Math.floor(
20026                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20027         );
20028         
20029         this.list.setWidth(lw); // default to '1'
20030
20031         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20032         //il.on('mouseover', this.onViewOver, this, { list:  i });
20033         //il.on('mousemove', this.onViewMove, this, { list:  i });
20034         il.setWidth(lw);
20035         il.setStyle({ 'overflow-x' : 'hidden'});
20036
20037         if(!this.tpl){
20038             this.tpl = new Roo.Template({
20039                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20040                 isEmpty: function (value, allValues) {
20041                     //Roo.log(value);
20042                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20043                     return dl ? 'has-children' : 'no-children'
20044                 }
20045             });
20046         }
20047         
20048         var store  = this.store;
20049         if (i > 0) {
20050             store  = new Roo.data.SimpleStore({
20051                 //fields : this.store.reader.meta.fields,
20052                 reader : this.store.reader,
20053                 data : [ ]
20054             });
20055         }
20056         this.stores[i]  = store;
20057                   
20058         var view = this.views[i] = new Roo.View(
20059             il,
20060             this.tpl,
20061             {
20062                 singleSelect:true,
20063                 store: store,
20064                 selectedClass: this.selectedClass
20065             }
20066         );
20067         view.getEl().setWidth(lw);
20068         view.getEl().setStyle({
20069             position: i < 1 ? 'relative' : 'absolute',
20070             top: 0,
20071             left: (i * lw ) + 'px',
20072             display : i > 0 ? 'none' : 'block'
20073         });
20074         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20075         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20076         //view.on('click', this.onViewClick, this, { list : i });
20077
20078         store.on('beforeload', this.onBeforeLoad, this);
20079         store.on('load',  this.onLoad, this, { list  : i});
20080         store.on('loadexception', this.onLoadException, this);
20081
20082         // hide the other vies..
20083         
20084         
20085         
20086     },
20087       
20088     restrictHeight : function()
20089     {
20090         var mh = 0;
20091         Roo.each(this.innerLists, function(il,i) {
20092             var el = this.views[i].getEl();
20093             el.dom.style.height = '';
20094             var inner = el.dom;
20095             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20096             // only adjust heights on other ones..
20097             mh = Math.max(h, mh);
20098             if (i < 1) {
20099                 
20100                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20101                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20102                
20103             }
20104             
20105             
20106         }, this);
20107         
20108         this.list.beginUpdate();
20109         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20110         this.list.alignTo(this.el, this.listAlign);
20111         this.list.endUpdate();
20112         
20113     },
20114      
20115     
20116     // -- store handlers..
20117     // private
20118     onBeforeLoad : function()
20119     {
20120         if(!this.hasFocus){
20121             return;
20122         }
20123         this.innerLists[0].update(this.loadingText ?
20124                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20125         this.restrictHeight();
20126         this.selectedIndex = -1;
20127     },
20128     // private
20129     onLoad : function(a,b,c,d)
20130     {
20131         if (!this.loadingChildren) {
20132             // then we are loading the top level. - hide the children
20133             for (var i = 1;i < this.views.length; i++) {
20134                 this.views[i].getEl().setStyle({ display : 'none' });
20135             }
20136             var lw = Math.floor(
20137                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20138             );
20139         
20140              this.list.setWidth(lw); // default to '1'
20141
20142             
20143         }
20144         if(!this.hasFocus){
20145             return;
20146         }
20147         
20148         if(this.store.getCount() > 0) {
20149             this.expand();
20150             this.restrictHeight();   
20151         } else {
20152             this.onEmptyResults();
20153         }
20154         
20155         if (!this.loadingChildren) {
20156             this.selectActive();
20157         }
20158         /*
20159         this.stores[1].loadData([]);
20160         this.stores[2].loadData([]);
20161         this.views
20162         */    
20163     
20164         //this.el.focus();
20165     },
20166     
20167     
20168     // private
20169     onLoadException : function()
20170     {
20171         this.collapse();
20172         Roo.log(this.store.reader.jsonData);
20173         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20174             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20175         }
20176         
20177         
20178     },
20179     // no cleaning of leading spaces on blur here.
20180     cleanLeadingSpace : function(e) { },
20181     
20182
20183     onSelectChange : function (view, sels, opts )
20184     {
20185         var ix = view.getSelectedIndexes();
20186          
20187         if (opts.list > this.maxColumns - 2) {
20188             if (view.store.getCount()<  1) {
20189                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20190
20191             } else  {
20192                 if (ix.length) {
20193                     // used to clear ?? but if we are loading unselected 
20194                     this.setFromData(view.store.getAt(ix[0]).data);
20195                 }
20196                 
20197             }
20198             
20199             return;
20200         }
20201         
20202         if (!ix.length) {
20203             // this get's fired when trigger opens..
20204            // this.setFromData({});
20205             var str = this.stores[opts.list+1];
20206             str.data.clear(); // removeall wihtout the fire events..
20207             return;
20208         }
20209         
20210         var rec = view.store.getAt(ix[0]);
20211          
20212         this.setFromData(rec.data);
20213         this.fireEvent('select', this, rec, ix[0]);
20214         
20215         var lw = Math.floor(
20216              (
20217                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20218              ) / this.maxColumns
20219         );
20220         this.loadingChildren = true;
20221         this.stores[opts.list+1].loadDataFromChildren( rec );
20222         this.loadingChildren = false;
20223         var dl = this.stores[opts.list+1]. getTotalCount();
20224         
20225         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20226         
20227         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20228         for (var i = opts.list+2; i < this.views.length;i++) {
20229             this.views[i].getEl().setStyle({ display : 'none' });
20230         }
20231         
20232         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20233         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20234         
20235         if (this.isLoading) {
20236            // this.selectActive(opts.list);
20237         }
20238          
20239     },
20240     
20241     
20242     
20243     
20244     onDoubleClick : function()
20245     {
20246         this.collapse(); //??
20247     },
20248     
20249      
20250     
20251     
20252     
20253     // private
20254     recordToStack : function(store, prop, value, stack)
20255     {
20256         var cstore = new Roo.data.SimpleStore({
20257             //fields : this.store.reader.meta.fields, // we need array reader.. for
20258             reader : this.store.reader,
20259             data : [ ]
20260         });
20261         var _this = this;
20262         var record  = false;
20263         var srec = false;
20264         if(store.getCount() < 1){
20265             return false;
20266         }
20267         store.each(function(r){
20268             if(r.data[prop] == value){
20269                 record = r;
20270             srec = r;
20271                 return false;
20272             }
20273             if (r.data.cn && r.data.cn.length) {
20274                 cstore.loadDataFromChildren( r);
20275                 var cret = _this.recordToStack(cstore, prop, value, stack);
20276                 if (cret !== false) {
20277                     record = cret;
20278                     srec = r;
20279                     return false;
20280                 }
20281             }
20282              
20283             return true;
20284         });
20285         if (record == false) {
20286             return false
20287         }
20288         stack.unshift(srec);
20289         return record;
20290     },
20291     
20292     /*
20293      * find the stack of stores that match our value.
20294      *
20295      * 
20296      */
20297     
20298     selectActive : function ()
20299     {
20300         // if store is not loaded, then we will need to wait for that to happen first.
20301         var stack = [];
20302         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20303         for (var i = 0; i < stack.length; i++ ) {
20304             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20305         }
20306         
20307     }
20308         
20309          
20310     
20311     
20312     
20313     
20314 });/*
20315  * Based on:
20316  * Ext JS Library 1.1.1
20317  * Copyright(c) 2006-2007, Ext JS, LLC.
20318  *
20319  * Originally Released Under LGPL - original licence link has changed is not relivant.
20320  *
20321  * Fork - LGPL
20322  * <script type="text/javascript">
20323  */
20324 /**
20325  * @class Roo.form.Checkbox
20326  * @extends Roo.form.Field
20327  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20328  * @constructor
20329  * Creates a new Checkbox
20330  * @param {Object} config Configuration options
20331  */
20332 Roo.form.Checkbox = function(config){
20333     Roo.form.Checkbox.superclass.constructor.call(this, config);
20334     this.addEvents({
20335         /**
20336          * @event check
20337          * Fires when the checkbox is checked or unchecked.
20338              * @param {Roo.form.Checkbox} this This checkbox
20339              * @param {Boolean} checked The new checked value
20340              */
20341         check : true
20342     });
20343 };
20344
20345 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20346     /**
20347      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20348      */
20349     focusClass : undefined,
20350     /**
20351      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20352      */
20353     fieldClass: "x-form-field",
20354     /**
20355      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20356      */
20357     checked: false,
20358     /**
20359      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20360      * {tag: "input", type: "checkbox", autocomplete: "off"})
20361      */
20362     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20363     /**
20364      * @cfg {String} boxLabel The text that appears beside the checkbox
20365      */
20366     boxLabel : "",
20367     /**
20368      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20369      */  
20370     inputValue : '1',
20371     /**
20372      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20373      */
20374      valueOff: '0', // value when not checked..
20375
20376     actionMode : 'viewEl', 
20377     //
20378     // private
20379     itemCls : 'x-menu-check-item x-form-item',
20380     groupClass : 'x-menu-group-item',
20381     inputType : 'hidden',
20382     
20383     
20384     inSetChecked: false, // check that we are not calling self...
20385     
20386     inputElement: false, // real input element?
20387     basedOn: false, // ????
20388     
20389     isFormField: true, // not sure where this is needed!!!!
20390
20391     onResize : function(){
20392         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20393         if(!this.boxLabel){
20394             this.el.alignTo(this.wrap, 'c-c');
20395         }
20396     },
20397
20398     initEvents : function(){
20399         Roo.form.Checkbox.superclass.initEvents.call(this);
20400         this.el.on("click", this.onClick,  this);
20401         this.el.on("change", this.onClick,  this);
20402     },
20403
20404
20405     getResizeEl : function(){
20406         return this.wrap;
20407     },
20408
20409     getPositionEl : function(){
20410         return this.wrap;
20411     },
20412
20413     // private
20414     onRender : function(ct, position){
20415         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20416         /*
20417         if(this.inputValue !== undefined){
20418             this.el.dom.value = this.inputValue;
20419         }
20420         */
20421         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20422         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20423         var viewEl = this.wrap.createChild({ 
20424             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20425         this.viewEl = viewEl;   
20426         this.wrap.on('click', this.onClick,  this); 
20427         
20428         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20429         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20430         
20431         
20432         
20433         if(this.boxLabel){
20434             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20435         //    viewEl.on('click', this.onClick,  this); 
20436         }
20437         //if(this.checked){
20438             this.setChecked(this.checked);
20439         //}else{
20440             //this.checked = this.el.dom;
20441         //}
20442
20443     },
20444
20445     // private
20446     initValue : Roo.emptyFn,
20447
20448     /**
20449      * Returns the checked state of the checkbox.
20450      * @return {Boolean} True if checked, else false
20451      */
20452     getValue : function(){
20453         if(this.el){
20454             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20455         }
20456         return this.valueOff;
20457         
20458     },
20459
20460         // private
20461     onClick : function(){ 
20462         if (this.disabled) {
20463             return;
20464         }
20465         this.setChecked(!this.checked);
20466
20467         //if(this.el.dom.checked != this.checked){
20468         //    this.setValue(this.el.dom.checked);
20469        // }
20470     },
20471
20472     /**
20473      * Sets the checked state of the checkbox.
20474      * On is always based on a string comparison between inputValue and the param.
20475      * @param {Boolean/String} value - the value to set 
20476      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20477      */
20478     setValue : function(v,suppressEvent){
20479         
20480         
20481         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20482         //if(this.el && this.el.dom){
20483         //    this.el.dom.checked = this.checked;
20484         //    this.el.dom.defaultChecked = this.checked;
20485         //}
20486         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20487         //this.fireEvent("check", this, this.checked);
20488     },
20489     // private..
20490     setChecked : function(state,suppressEvent)
20491     {
20492         if (this.inSetChecked) {
20493             this.checked = state;
20494             return;
20495         }
20496         
20497     
20498         if(this.wrap){
20499             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20500         }
20501         this.checked = state;
20502         if(suppressEvent !== true){
20503             this.fireEvent('check', this, state);
20504         }
20505         this.inSetChecked = true;
20506         this.el.dom.value = state ? this.inputValue : this.valueOff;
20507         this.inSetChecked = false;
20508         
20509     },
20510     // handle setting of hidden value by some other method!!?!?
20511     setFromHidden: function()
20512     {
20513         if(!this.el){
20514             return;
20515         }
20516         //console.log("SET FROM HIDDEN");
20517         //alert('setFrom hidden');
20518         this.setValue(this.el.dom.value);
20519     },
20520     
20521     onDestroy : function()
20522     {
20523         if(this.viewEl){
20524             Roo.get(this.viewEl).remove();
20525         }
20526          
20527         Roo.form.Checkbox.superclass.onDestroy.call(this);
20528     },
20529     
20530     setBoxLabel : function(str)
20531     {
20532         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20533     }
20534
20535 });/*
20536  * Based on:
20537  * Ext JS Library 1.1.1
20538  * Copyright(c) 2006-2007, Ext JS, LLC.
20539  *
20540  * Originally Released Under LGPL - original licence link has changed is not relivant.
20541  *
20542  * Fork - LGPL
20543  * <script type="text/javascript">
20544  */
20545  
20546 /**
20547  * @class Roo.form.Radio
20548  * @extends Roo.form.Checkbox
20549  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20550  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20551  * @constructor
20552  * Creates a new Radio
20553  * @param {Object} config Configuration options
20554  */
20555 Roo.form.Radio = function(){
20556     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20557 };
20558 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20559     inputType: 'radio',
20560
20561     /**
20562      * If this radio is part of a group, it will return the selected value
20563      * @return {String}
20564      */
20565     getGroupValue : function(){
20566         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20567     },
20568     
20569     
20570     onRender : function(ct, position){
20571         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20572         
20573         if(this.inputValue !== undefined){
20574             this.el.dom.value = this.inputValue;
20575         }
20576          
20577         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20578         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20579         //var viewEl = this.wrap.createChild({ 
20580         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20581         //this.viewEl = viewEl;   
20582         //this.wrap.on('click', this.onClick,  this); 
20583         
20584         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20585         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20586         
20587         
20588         
20589         if(this.boxLabel){
20590             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20591         //    viewEl.on('click', this.onClick,  this); 
20592         }
20593          if(this.checked){
20594             this.el.dom.checked =   'checked' ;
20595         }
20596          
20597     } 
20598     
20599     
20600 });Roo.rtf = {}; // namespace
20601 Roo.rtf.Hex = function(hex)
20602 {
20603     this.hexstr = hex;
20604 };
20605 Roo.rtf.Paragraph = function(opts)
20606 {
20607     this.content = []; ///??? is that used?
20608 };Roo.rtf.Span = function(opts)
20609 {
20610     this.value = opts.value;
20611 };
20612
20613 Roo.rtf.Group = function(parent)
20614 {
20615     // we dont want to acutally store parent - it will make debug a nightmare..
20616     this.content = [];
20617     this.cn  = [];
20618      
20619        
20620     
20621 };
20622
20623 Roo.rtf.Group.prototype = {
20624     ignorable : false,
20625     content: false,
20626     cn: false,
20627     addContent : function(node) {
20628         // could set styles...
20629         this.content.push(node);
20630     },
20631     addChild : function(cn)
20632     {
20633         this.cn.push(cn);
20634     },
20635     // only for images really...
20636     toDataURL : function()
20637     {
20638         var mimetype = false;
20639         switch(true) {
20640             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20641                 mimetype = "image/png";
20642                 break;
20643              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20644                 mimetype = "image/jpeg";
20645                 break;
20646             default :
20647                 return 'about:blank'; // ?? error?
20648         }
20649         
20650         
20651         var hexstring = this.content[this.content.length-1].value;
20652         
20653         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20654             return String.fromCharCode(parseInt(a, 16));
20655         }).join(""));
20656     }
20657     
20658 };
20659 // this looks like it's normally the {rtf{ .... }}
20660 Roo.rtf.Document = function()
20661 {
20662     // we dont want to acutally store parent - it will make debug a nightmare..
20663     this.rtlch  = [];
20664     this.content = [];
20665     this.cn = [];
20666     
20667 };
20668 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20669     addChild : function(cn)
20670     {
20671         this.cn.push(cn);
20672         switch(cn.type) {
20673             case 'rtlch': // most content seems to be inside this??
20674             case 'listtext':
20675             case 'shpinst':
20676                 this.rtlch.push(cn);
20677                 return;
20678             default:
20679                 this[cn.type] = cn;
20680         }
20681         
20682     },
20683     
20684     getElementsByType : function(type)
20685     {
20686         var ret =  [];
20687         this._getElementsByType(type, ret, this.cn, 'rtf');
20688         return ret;
20689     },
20690     _getElementsByType : function (type, ret, search_array, path)
20691     {
20692         search_array.forEach(function(n,i) {
20693             if (n.type == type) {
20694                 n.path = path + '/' + n.type + ':' + i;
20695                 ret.push(n);
20696             }
20697             if (n.cn.length > 0) {
20698                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20699             }
20700         },this);
20701     }
20702     
20703 });
20704  
20705 Roo.rtf.Ctrl = function(opts)
20706 {
20707     this.value = opts.value;
20708     this.param = opts.param;
20709 };
20710 /**
20711  *
20712  *
20713  * based on this https://github.com/iarna/rtf-parser
20714  * it's really only designed to extract pict from pasted RTF 
20715  *
20716  * usage:
20717  *
20718  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20719  *  
20720  *
20721  */
20722
20723  
20724
20725
20726
20727 Roo.rtf.Parser = function(text) {
20728     //super({objectMode: true})
20729     this.text = '';
20730     this.parserState = this.parseText;
20731     
20732     // these are for interpeter...
20733     this.doc = {};
20734     ///this.parserState = this.parseTop
20735     this.groupStack = [];
20736     this.hexStore = [];
20737     this.doc = false;
20738     
20739     this.groups = []; // where we put the return.
20740     
20741     for (var ii = 0; ii < text.length; ++ii) {
20742         ++this.cpos;
20743         
20744         if (text[ii] === '\n') {
20745             ++this.row;
20746             this.col = 1;
20747         } else {
20748             ++this.col;
20749         }
20750         this.parserState(text[ii]);
20751     }
20752     
20753     
20754     
20755 };
20756 Roo.rtf.Parser.prototype = {
20757     text : '', // string being parsed..
20758     controlWord : '',
20759     controlWordParam :  '',
20760     hexChar : '',
20761     doc : false,
20762     group: false,
20763     groupStack : false,
20764     hexStore : false,
20765     
20766     
20767     cpos : 0, 
20768     row : 1, // reportin?
20769     col : 1, //
20770
20771      
20772     push : function (el)
20773     {
20774         var m = 'cmd'+ el.type;
20775         if (typeof(this[m]) == 'undefined') {
20776             Roo.log('invalid cmd:' + el.type);
20777             return;
20778         }
20779         this[m](el);
20780         //Roo.log(el);
20781     },
20782     flushHexStore : function()
20783     {
20784         if (this.hexStore.length < 1) {
20785             return;
20786         }
20787         var hexstr = this.hexStore.map(
20788             function(cmd) {
20789                 return cmd.value;
20790         }).join('');
20791         
20792         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20793               
20794             
20795         this.hexStore.splice(0)
20796         
20797     },
20798     
20799     cmdgroupstart : function()
20800     {
20801         this.flushHexStore();
20802         if (this.group) {
20803             this.groupStack.push(this.group);
20804         }
20805          // parent..
20806         if (this.doc === false) {
20807             this.group = this.doc = new Roo.rtf.Document();
20808             return;
20809             
20810         }
20811         this.group = new Roo.rtf.Group(this.group);
20812     },
20813     cmdignorable : function()
20814     {
20815         this.flushHexStore();
20816         this.group.ignorable = true;
20817     },
20818     cmdendparagraph : function()
20819     {
20820         this.flushHexStore();
20821         this.group.addContent(new Roo.rtf.Paragraph());
20822     },
20823     cmdgroupend : function ()
20824     {
20825         this.flushHexStore();
20826         var endingGroup = this.group;
20827         
20828         
20829         this.group = this.groupStack.pop();
20830         if (this.group) {
20831             this.group.addChild(endingGroup);
20832         }
20833         
20834         
20835         
20836         var doc = this.group || this.doc;
20837         //if (endingGroup instanceof FontTable) {
20838         //  doc.fonts = endingGroup.table
20839         //} else if (endingGroup instanceof ColorTable) {
20840         //  doc.colors = endingGroup.table
20841         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20842         if (endingGroup.ignorable === false) {
20843             //code
20844             this.groups.push(endingGroup);
20845            // Roo.log( endingGroup );
20846         }
20847             //Roo.each(endingGroup.content, function(item)) {
20848             //    doc.addContent(item);
20849             //}
20850             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20851         //}
20852     },
20853     cmdtext : function (cmd)
20854     {
20855         this.flushHexStore();
20856         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20857             //this.group = this.doc
20858         }
20859         this.group.addContent(new Roo.rtf.Span(cmd));
20860     },
20861     cmdcontrolword : function (cmd)
20862     {
20863         this.flushHexStore();
20864         if (!this.group.type) {
20865             this.group.type = cmd.value;
20866             return;
20867         }
20868         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20869         // we actually don't care about ctrl words...
20870         return ;
20871         /*
20872         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20873         if (this[method]) {
20874             this[method](cmd.param)
20875         } else {
20876             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20877         }
20878         */
20879     },
20880     cmdhexchar : function(cmd) {
20881         this.hexStore.push(cmd);
20882     },
20883     cmderror : function(cmd) {
20884         throw new Exception (cmd.value);
20885     },
20886     
20887     /*
20888       _flush (done) {
20889         if (this.text !== '\u0000') this.emitText()
20890         done()
20891       }
20892       */
20893       
20894       
20895     parseText : function(c)
20896     {
20897         if (c === '\\') {
20898             this.parserState = this.parseEscapes;
20899         } else if (c === '{') {
20900             this.emitStartGroup();
20901         } else if (c === '}') {
20902             this.emitEndGroup();
20903         } else if (c === '\x0A' || c === '\x0D') {
20904             // cr/lf are noise chars
20905         } else {
20906             this.text += c;
20907         }
20908     },
20909     
20910     parseEscapes: function (c)
20911     {
20912         if (c === '\\' || c === '{' || c === '}') {
20913             this.text += c;
20914             this.parserState = this.parseText;
20915         } else {
20916             this.parserState = this.parseControlSymbol;
20917             this.parseControlSymbol(c);
20918         }
20919     },
20920     parseControlSymbol: function(c)
20921     {
20922         if (c === '~') {
20923             this.text += '\u00a0'; // nbsp
20924             this.parserState = this.parseText
20925         } else if (c === '-') {
20926              this.text += '\u00ad'; // soft hyphen
20927         } else if (c === '_') {
20928             this.text += '\u2011'; // non-breaking hyphen
20929         } else if (c === '*') {
20930             this.emitIgnorable();
20931             this.parserState = this.parseText;
20932         } else if (c === "'") {
20933             this.parserState = this.parseHexChar;
20934         } else if (c === '|') { // formula cacter
20935             this.emitFormula();
20936             this.parserState = this.parseText;
20937         } else if (c === ':') { // subentry in an index entry
20938             this.emitIndexSubEntry();
20939             this.parserState = this.parseText;
20940         } else if (c === '\x0a') {
20941             this.emitEndParagraph();
20942             this.parserState = this.parseText;
20943         } else if (c === '\x0d') {
20944             this.emitEndParagraph();
20945             this.parserState = this.parseText;
20946         } else {
20947             this.parserState = this.parseControlWord;
20948             this.parseControlWord(c);
20949         }
20950     },
20951     parseHexChar: function (c)
20952     {
20953         if (/^[A-Fa-f0-9]$/.test(c)) {
20954             this.hexChar += c;
20955             if (this.hexChar.length >= 2) {
20956               this.emitHexChar();
20957               this.parserState = this.parseText;
20958             }
20959             return;
20960         }
20961         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20962         this.parserState = this.parseText;
20963         
20964     },
20965     parseControlWord : function(c)
20966     {
20967         if (c === ' ') {
20968             this.emitControlWord();
20969             this.parserState = this.parseText;
20970         } else if (/^[-\d]$/.test(c)) {
20971             this.parserState = this.parseControlWordParam;
20972             this.controlWordParam += c;
20973         } else if (/^[A-Za-z]$/.test(c)) {
20974           this.controlWord += c;
20975         } else {
20976           this.emitControlWord();
20977           this.parserState = this.parseText;
20978           this.parseText(c);
20979         }
20980     },
20981     parseControlWordParam : function (c) {
20982         if (/^\d$/.test(c)) {
20983           this.controlWordParam += c;
20984         } else if (c === ' ') {
20985           this.emitControlWord();
20986           this.parserState = this.parseText;
20987         } else {
20988           this.emitControlWord();
20989           this.parserState = this.parseText;
20990           this.parseText(c);
20991         }
20992     },
20993     
20994     
20995     
20996     
20997     emitText : function () {
20998         if (this.text === '') {
20999             return;
21000         }
21001         this.push({
21002             type: 'text',
21003             value: this.text,
21004             pos: this.cpos,
21005             row: this.row,
21006             col: this.col
21007         });
21008         this.text = ''
21009     },
21010     emitControlWord : function ()
21011     {
21012         this.emitText();
21013         if (this.controlWord === '') {
21014             this.emitError('empty control word');
21015         } else {
21016             this.push({
21017                   type: 'controlword',
21018                   value: this.controlWord,
21019                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
21020                   pos: this.cpos,
21021                   row: this.row,
21022                   col: this.col
21023             });
21024         }
21025         this.controlWord = '';
21026         this.controlWordParam = '';
21027     },
21028     emitStartGroup : function ()
21029     {
21030         this.emitText();
21031         this.push({
21032             type: 'groupstart',
21033             pos: this.cpos,
21034             row: this.row,
21035             col: this.col
21036         });
21037     },
21038     emitEndGroup : function ()
21039     {
21040         this.emitText();
21041         this.push({
21042             type: 'groupend',
21043             pos: this.cpos,
21044             row: this.row,
21045             col: this.col
21046         });
21047     },
21048     emitIgnorable : function ()
21049     {
21050         this.emitText();
21051         this.push({
21052             type: 'ignorable',
21053             pos: this.cpos,
21054             row: this.row,
21055             col: this.col
21056         });
21057     },
21058     emitHexChar : function ()
21059     {
21060         this.emitText();
21061         this.push({
21062             type: 'hexchar',
21063             value: this.hexChar,
21064             pos: this.cpos,
21065             row: this.row,
21066             col: this.col
21067         });
21068         this.hexChar = ''
21069     },
21070     emitError : function (message)
21071     {
21072       this.emitText();
21073       this.push({
21074             type: 'error',
21075             value: message,
21076             row: this.row,
21077             col: this.col,
21078             char: this.cpos //,
21079             //stack: new Error().stack
21080         });
21081     },
21082     emitEndParagraph : function () {
21083         this.emitText();
21084         this.push({
21085             type: 'endparagraph',
21086             pos: this.cpos,
21087             row: this.row,
21088             col: this.col
21089         });
21090     }
21091      
21092 } ;
21093 Roo.htmleditor = {};
21094  
21095 /**
21096  * @class Roo.htmleditor.Filter
21097  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21098  * @cfg {DomElement} node The node to iterate and filter
21099  * @cfg {boolean|String|Array} tag Tags to replace 
21100  * @constructor
21101  * Create a new Filter.
21102  * @param {Object} config Configuration options
21103  */
21104
21105
21106
21107 Roo.htmleditor.Filter = function(cfg) {
21108     Roo.apply(this.cfg);
21109     // this does not actually call walk as it's really just a abstract class
21110 }
21111
21112
21113 Roo.htmleditor.Filter.prototype = {
21114     
21115     node: false,
21116     
21117     tag: false,
21118
21119     // overrride to do replace comments.
21120     replaceComment : false,
21121     
21122     // overrride to do replace or do stuff with tags..
21123     replaceTag : false,
21124     
21125     walk : function(dom)
21126     {
21127         Roo.each( Array.from(dom.childNodes), function( e ) {
21128             switch(true) {
21129                 
21130                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
21131                     this.replaceComment(e);
21132                     return;
21133                 
21134                 case e.nodeType != 1: //not a node.
21135                     return;
21136                 
21137                 case this.tag === true: // everything
21138                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21139                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21140                     if (this.replaceTag && false === this.replaceTag(e)) {
21141                         return;
21142                     }
21143                     if (e.hasChildNodes()) {
21144                         this.walk(e);
21145                     }
21146                     return;
21147                 
21148                 default:    // tags .. that do not match.
21149                     if (e.hasChildNodes()) {
21150                         this.walk(e);
21151                     }
21152             }
21153             
21154         }, this);
21155         
21156     }
21157 }; 
21158
21159 /**
21160  * @class Roo.htmleditor.FilterAttributes
21161  * clean attributes and  styles including http:// etc.. in attribute
21162  * @constructor
21163 * Run a new Attribute Filter
21164 * @param {Object} config Configuration options
21165  */
21166 Roo.htmleditor.FilterAttributes = function(cfg)
21167 {
21168     Roo.apply(this, cfg);
21169     this.attrib_black = this.attrib_black || [];
21170     this.attrib_white = this.attrib_white || [];
21171
21172     this.attrib_clean = this.attrib_clean || [];
21173     this.style_white = this.style_white || [];
21174     this.style_black = this.style_black || [];
21175     this.walk(cfg.node);
21176 }
21177
21178 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21179 {
21180     tag: true, // all tags
21181     
21182     attrib_black : false, // array
21183     attrib_clean : false,
21184     attrib_white : false,
21185
21186     style_white : false,
21187     style_black : false,
21188      
21189      
21190     replaceTag : function(node)
21191     {
21192         if (!node.attributes || !node.attributes.length) {
21193             return true;
21194         }
21195         
21196         for (var i = node.attributes.length-1; i > -1 ; i--) {
21197             var a = node.attributes[i];
21198             //console.log(a);
21199             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21200                 node.removeAttribute(a.name);
21201                 continue;
21202             }
21203             
21204             
21205             
21206             if (a.name.toLowerCase().substr(0,2)=='on')  {
21207                 node.removeAttribute(a.name);
21208                 continue;
21209             }
21210             
21211             
21212             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21213                 node.removeAttribute(a.name);
21214                 continue;
21215             }
21216             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21217                 this.cleanAttr(node,a.name,a.value); // fixme..
21218                 continue;
21219             }
21220             if (a.name == 'style') {
21221                 this.cleanStyle(node,a.name,a.value);
21222                 continue;
21223             }
21224             /// clean up MS crap..
21225             // tecnically this should be a list of valid class'es..
21226             
21227             
21228             if (a.name == 'class') {
21229                 if (a.value.match(/^Mso/)) {
21230                     node.removeAttribute('class');
21231                 }
21232                 
21233                 if (a.value.match(/^body$/)) {
21234                     node.removeAttribute('class');
21235                 }
21236                 continue;
21237             }
21238             
21239             
21240             // style cleanup!?
21241             // class cleanup?
21242             
21243         }
21244         return true; // clean children
21245     },
21246         
21247     cleanAttr: function(node, n,v)
21248     {
21249         
21250         if (v.match(/^\./) || v.match(/^\//)) {
21251             return;
21252         }
21253         if (v.match(/^(http|https):\/\//)
21254             || v.match(/^mailto:/) 
21255             || v.match(/^ftp:/)
21256             || v.match(/^data:/)
21257             ) {
21258             return;
21259         }
21260         if (v.match(/^#/)) {
21261             return;
21262         }
21263         if (v.match(/^\{/)) { // allow template editing.
21264             return;
21265         }
21266 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21267         node.removeAttribute(n);
21268         
21269     },
21270     cleanStyle : function(node,  n,v)
21271     {
21272         if (v.match(/expression/)) { //XSS?? should we even bother..
21273             node.removeAttribute(n);
21274             return;
21275         }
21276         
21277         var parts = v.split(/;/);
21278         var clean = [];
21279         
21280         Roo.each(parts, function(p) {
21281             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21282             if (!p.length) {
21283                 return true;
21284             }
21285             var l = p.split(':').shift().replace(/\s+/g,'');
21286             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21287             
21288             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21289                 return true;
21290             }
21291             //Roo.log()
21292             // only allow 'c whitelisted system attributes'
21293             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21294                 return true;
21295             }
21296             
21297             
21298             clean.push(p);
21299             return true;
21300         },this);
21301         if (clean.length) { 
21302             node.setAttribute(n, clean.join(';'));
21303         } else {
21304             node.removeAttribute(n);
21305         }
21306         
21307     }
21308         
21309         
21310         
21311     
21312 });/**
21313  * @class Roo.htmleditor.FilterBlack
21314  * remove blacklisted elements.
21315  * @constructor
21316  * Run a new Blacklisted Filter
21317  * @param {Object} config Configuration options
21318  */
21319
21320 Roo.htmleditor.FilterBlack = function(cfg)
21321 {
21322     Roo.apply(this, cfg);
21323     this.walk(cfg.node);
21324 }
21325
21326 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21327 {
21328     tag : true, // all elements.
21329    
21330     replaceTag : function(n)
21331     {
21332         n.parentNode.removeChild(n);
21333     }
21334 });
21335 /**
21336  * @class Roo.htmleditor.FilterComment
21337  * remove comments.
21338  * @constructor
21339 * Run a new Comments Filter
21340 * @param {Object} config Configuration options
21341  */
21342 Roo.htmleditor.FilterComment = function(cfg)
21343 {
21344     this.walk(cfg.node);
21345 }
21346
21347 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21348 {
21349   
21350     replaceComment : function(n)
21351     {
21352         n.parentNode.removeChild(n);
21353     }
21354 });/**
21355  * @class Roo.htmleditor.FilterKeepChildren
21356  * remove tags but keep children
21357  * @constructor
21358  * Run a new Keep Children Filter
21359  * @param {Object} config Configuration options
21360  */
21361
21362 Roo.htmleditor.FilterKeepChildren = function(cfg)
21363 {
21364     Roo.apply(this, cfg);
21365     if (this.tag === false) {
21366         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21367     }
21368     this.walk(cfg.node);
21369 }
21370
21371 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21372 {
21373     
21374   
21375     replaceTag : function(node)
21376     {
21377         // walk children...
21378         //Roo.log(node);
21379         var ar = Array.from(node.childNodes);
21380         //remove first..
21381         for (var i = 0; i < ar.length; i++) {
21382             if (ar[i].nodeType == 1) {
21383                 if (
21384                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21385                     || // array and it matches
21386                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21387                 ) {
21388                     this.replaceTag(ar[i]); // child is blacklisted as well...
21389                     continue;
21390                 }
21391             }
21392         }  
21393         ar = Array.from(node.childNodes);
21394         for (var i = 0; i < ar.length; i++) {
21395          
21396             node.removeChild(ar[i]);
21397             // what if we need to walk these???
21398             node.parentNode.insertBefore(ar[i], node);
21399             if (this.tag !== false) {
21400                 this.walk(ar[i]);
21401                 
21402             }
21403         }
21404         node.parentNode.removeChild(node);
21405         return false; // don't walk children
21406         
21407         
21408     }
21409 });/**
21410  * @class Roo.htmleditor.FilterParagraph
21411  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21412  * like on 'push' to remove the <p> tags and replace them with line breaks.
21413  * @constructor
21414  * Run a new Paragraph Filter
21415  * @param {Object} config Configuration options
21416  */
21417
21418 Roo.htmleditor.FilterParagraph = function(cfg)
21419 {
21420     // no need to apply config.
21421     this.walk(cfg.node);
21422 }
21423
21424 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21425 {
21426     
21427      
21428     tag : 'P',
21429     
21430      
21431     replaceTag : function(node)
21432     {
21433         
21434         if (node.childNodes.length == 1 &&
21435             node.childNodes[0].nodeType == 3 &&
21436             node.childNodes[0].textContent.trim().length < 1
21437             ) {
21438             // remove and replace with '<BR>';
21439             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21440             return false; // no need to walk..
21441         }
21442         var ar = Array.from(node.childNodes);
21443         for (var i = 0; i < ar.length; i++) {
21444             node.removeChild(ar[i]);
21445             // what if we need to walk these???
21446             node.parentNode.insertBefore(ar[i], node);
21447         }
21448         // now what about this?
21449         // <p> &nbsp; </p>
21450         
21451         // double BR.
21452         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21453         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21454         node.parentNode.removeChild(node);
21455         
21456         return false;
21457
21458     }
21459     
21460 });/**
21461  * @class Roo.htmleditor.FilterSpan
21462  * filter span's with no attributes out..
21463  * @constructor
21464  * Run a new Span Filter
21465  * @param {Object} config Configuration options
21466  */
21467
21468 Roo.htmleditor.FilterSpan = function(cfg)
21469 {
21470     // no need to apply config.
21471     this.walk(cfg.node);
21472 }
21473
21474 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21475 {
21476      
21477     tag : 'SPAN',
21478      
21479  
21480     replaceTag : function(node)
21481     {
21482         if (node.attributes && node.attributes.length > 0) {
21483             return true; // walk if there are any.
21484         }
21485         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21486         return false;
21487      
21488     }
21489     
21490 });/**
21491  * @class Roo.htmleditor.FilterTableWidth
21492   try and remove table width data - as that frequently messes up other stuff.
21493  * 
21494  *      was cleanTableWidths.
21495  *
21496  * Quite often pasting from word etc.. results in tables with column and widths.
21497  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21498  *
21499  * @constructor
21500  * Run a new Table Filter
21501  * @param {Object} config Configuration options
21502  */
21503
21504 Roo.htmleditor.FilterTableWidth = function(cfg)
21505 {
21506     // no need to apply config.
21507     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21508     this.walk(cfg.node);
21509 }
21510
21511 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21512 {
21513      
21514      
21515     
21516     replaceTag: function(node) {
21517         
21518         
21519       
21520         if (node.hasAttribute('width')) {
21521             node.removeAttribute('width');
21522         }
21523         
21524          
21525         if (node.hasAttribute("style")) {
21526             // pretty basic...
21527             
21528             var styles = node.getAttribute("style").split(";");
21529             var nstyle = [];
21530             Roo.each(styles, function(s) {
21531                 if (!s.match(/:/)) {
21532                     return;
21533                 }
21534                 var kv = s.split(":");
21535                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21536                     return;
21537                 }
21538                 // what ever is left... we allow.
21539                 nstyle.push(s);
21540             });
21541             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21542             if (!nstyle.length) {
21543                 node.removeAttribute('style');
21544             }
21545         }
21546         
21547         return true; // continue doing children..
21548     }
21549 });/**
21550  * @class Roo.htmleditor.FilterWord
21551  * try and clean up all the mess that Word generates.
21552  * 
21553  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21554  
21555  * @constructor
21556  * Run a new Span Filter
21557  * @param {Object} config Configuration options
21558  */
21559
21560 Roo.htmleditor.FilterWord = function(cfg)
21561 {
21562     // no need to apply config.
21563     this.walk(cfg.node);
21564 }
21565
21566 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21567 {
21568     tag: true,
21569      
21570     
21571     /**
21572      * Clean up MS wordisms...
21573      */
21574     replaceTag : function(node)
21575     {
21576          
21577         // no idea what this does - span with text, replaceds with just text.
21578         if(
21579                 node.nodeName == 'SPAN' &&
21580                 !node.hasAttributes() &&
21581                 node.childNodes.length == 1 &&
21582                 node.firstChild.nodeName == "#text"  
21583         ) {
21584             var textNode = node.firstChild;
21585             node.removeChild(textNode);
21586             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21587                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21588             }
21589             node.parentNode.insertBefore(textNode, node);
21590             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21591                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21592             }
21593             
21594             node.parentNode.removeChild(node);
21595             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21596         }
21597         
21598    
21599         
21600         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21601             node.parentNode.removeChild(node);
21602             return false; // dont do chidlren
21603         }
21604         //Roo.log(node.tagName);
21605         // remove - but keep children..
21606         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21607             //Roo.log('-- removed');
21608             while (node.childNodes.length) {
21609                 var cn = node.childNodes[0];
21610                 node.removeChild(cn);
21611                 node.parentNode.insertBefore(cn, node);
21612                 // move node to parent - and clean it..
21613                 this.replaceTag(cn);
21614             }
21615             node.parentNode.removeChild(node);
21616             /// no need to iterate chidlren = it's got none..
21617             //this.iterateChildren(node, this.cleanWord);
21618             return false; // no need to iterate children.
21619         }
21620         // clean styles
21621         if (node.className.length) {
21622             
21623             var cn = node.className.split(/\W+/);
21624             var cna = [];
21625             Roo.each(cn, function(cls) {
21626                 if (cls.match(/Mso[a-zA-Z]+/)) {
21627                     return;
21628                 }
21629                 cna.push(cls);
21630             });
21631             node.className = cna.length ? cna.join(' ') : '';
21632             if (!cna.length) {
21633                 node.removeAttribute("class");
21634             }
21635         }
21636         
21637         if (node.hasAttribute("lang")) {
21638             node.removeAttribute("lang");
21639         }
21640         
21641         if (node.hasAttribute("style")) {
21642             
21643             var styles = node.getAttribute("style").split(";");
21644             var nstyle = [];
21645             Roo.each(styles, function(s) {
21646                 if (!s.match(/:/)) {
21647                     return;
21648                 }
21649                 var kv = s.split(":");
21650                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21651                     return;
21652                 }
21653                 // what ever is left... we allow.
21654                 nstyle.push(s);
21655             });
21656             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21657             if (!nstyle.length) {
21658                 node.removeAttribute('style');
21659             }
21660         }
21661         return true; // do children
21662         
21663         
21664         
21665     }
21666 });
21667 /**
21668  * @class Roo.htmleditor.FilterStyleToTag
21669  * part of the word stuff... - certain 'styles' should be converted to tags.
21670  * eg.
21671  *   font-weight: bold -> bold
21672  *   ?? super / subscrit etc..
21673  * 
21674  * @constructor
21675 * Run a new style to tag filter.
21676 * @param {Object} config Configuration options
21677  */
21678 Roo.htmleditor.FilterStyleToTag = function(cfg)
21679 {
21680     
21681     this.tags = {
21682         B  : [ 'fontWeight' , 'bold'],
21683         I :  [ 'fontStyle' , 'italic'],
21684         //pre :  [ 'font-style' , 'italic'],
21685         // h1.. h6 ?? font-size?
21686         SUP : [ 'verticalAlign' , 'super' ],
21687         SUB : [ 'verticalAlign' , 'sub' ]
21688         
21689         
21690     };
21691     
21692     Roo.apply(this, cfg);
21693      
21694     
21695     this.walk(cfg.node);
21696     
21697     
21698     
21699 }
21700
21701
21702 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21703 {
21704     tag: true, // all tags
21705     
21706     tags : false,
21707     
21708     
21709     replaceTag : function(node)
21710     {
21711         
21712         
21713         if (node.getAttribute("style") === null) {
21714             return true;
21715         }
21716         var inject = [];
21717         for (var k in this.tags) {
21718             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21719                 inject.push(k);
21720                 node.style.removeProperty(this.tags[k][0]);
21721             }
21722         }
21723         if (!inject.length) {
21724             return true; 
21725         }
21726         var cn = Array.from(node.childNodes);
21727         var nn = node;
21728         Roo.each(inject, function(t) {
21729             var nc = node.ownerDocument.createElement(t);
21730             nn.appendChild(nc);
21731             nn = nc;
21732         });
21733         for(var i = 0;i < cn.length;cn++) {
21734             node.removeChild(cn[i]);
21735             nn.appendChild(cn[i]);
21736         }
21737         return true /// iterate thru
21738     }
21739     
21740 })/**
21741  * @class Roo.htmleditor.FilterLongBr
21742  * BR/BR/BR - keep a maximum of 2...
21743  * @constructor
21744  * Run a new Long BR Filter
21745  * @param {Object} config Configuration options
21746  */
21747
21748 Roo.htmleditor.FilterLongBr = function(cfg)
21749 {
21750     // no need to apply config.
21751     this.walk(cfg.node);
21752 }
21753
21754 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21755 {
21756     
21757      
21758     tag : 'BR',
21759     
21760      
21761     replaceTag : function(node)
21762     {
21763         
21764         var ps = node.nextSibling;
21765         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21766             ps = ps.nextSibling;
21767         }
21768         
21769         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21770             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21771             return false;
21772         }
21773         
21774         if (!ps || ps.nodeType != 1) {
21775             return false;
21776         }
21777         
21778         if (!ps || ps.tagName != 'BR') {
21779            
21780             return false;
21781         }
21782         
21783         
21784         
21785         
21786         
21787         if (!node.previousSibling) {
21788             return false;
21789         }
21790         var ps = node.previousSibling;
21791         
21792         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21793             ps = ps.previousSibling;
21794         }
21795         if (!ps || ps.nodeType != 1) {
21796             return false;
21797         }
21798         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21799         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21800             return false;
21801         }
21802         
21803         node.parentNode.removeChild(node); // remove me...
21804         
21805         return false; // no need to do children
21806
21807     }
21808     
21809 }); 
21810
21811 /**
21812  * @class Roo.htmleditor.FilterBlock
21813  * removes id / data-block and contenteditable that are associated with blocks
21814  * usage should be done on a cloned copy of the dom
21815  * @constructor
21816 * Run a new Attribute Filter { node : xxxx }}
21817 * @param {Object} config Configuration options
21818  */
21819 Roo.htmleditor.FilterBlock = function(cfg)
21820 {
21821     Roo.apply(this, cfg);
21822     var qa = cfg.node.querySelectorAll;
21823     this.removeAttributes('data-block');
21824     this.removeAttributes('contenteditable');
21825     this.removeAttributes('id');
21826     
21827 }
21828
21829 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
21830 {
21831     node: true, // all tags
21832      
21833      
21834     removeAttributes : function(attr)
21835     {
21836         var ar = this.node.querySelectorAll('*[' + attr + ']');
21837         for (var i =0;i<ar.length;i++) {
21838             ar[i].removeAttribute(attr);
21839         }
21840     }
21841         
21842         
21843         
21844     
21845 });
21846 /***
21847  * This is based loosely on tinymce 
21848  * @class Roo.htmleditor.TidySerializer
21849  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21850  * @constructor
21851  * @method Serializer
21852  * @param {Object} settings Name/value settings object.
21853  */
21854
21855
21856 Roo.htmleditor.TidySerializer = function(settings)
21857 {
21858     Roo.apply(this, settings);
21859     
21860     this.writer = new Roo.htmleditor.TidyWriter(settings);
21861     
21862     
21863
21864 };
21865 Roo.htmleditor.TidySerializer.prototype = {
21866     
21867     /**
21868      * @param {boolean} inner do the inner of the node.
21869      */
21870     inner : false,
21871     
21872     writer : false,
21873     
21874     /**
21875     * Serializes the specified node into a string.
21876     *
21877     * @example
21878     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
21879     * @method serialize
21880     * @param {DomElement} node Node instance to serialize.
21881     * @return {String} String with HTML based on DOM tree.
21882     */
21883     serialize : function(node) {
21884         
21885         // = settings.validate;
21886         var writer = this.writer;
21887         var self  = this;
21888         this.handlers = {
21889             // #text
21890             3: function(node) {
21891                 
21892                 writer.text(node.nodeValue, node);
21893             },
21894             // #comment
21895             8: function(node) {
21896                 writer.comment(node.nodeValue);
21897             },
21898             // Processing instruction
21899             7: function(node) {
21900                 writer.pi(node.name, node.nodeValue);
21901             },
21902             // Doctype
21903             10: function(node) {
21904                 writer.doctype(node.nodeValue);
21905             },
21906             // CDATA
21907             4: function(node) {
21908                 writer.cdata(node.nodeValue);
21909             },
21910             // Document fragment
21911             11: function(node) {
21912                 node = node.firstChild;
21913                 if (!node) {
21914                     return;
21915                 }
21916                 while(node) {
21917                     self.walk(node);
21918                     node = node.nextSibling
21919                 }
21920             }
21921         };
21922         writer.reset();
21923         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
21924         return writer.getContent();
21925     },
21926
21927     walk: function(node)
21928     {
21929         var attrName, attrValue, sortedAttrs, i, l, elementRule,
21930             handler = this.handlers[node.nodeType];
21931             
21932         if (handler) {
21933             handler(node);
21934             return;
21935         }
21936     
21937         var name = node.nodeName;
21938         var isEmpty = node.childNodes.length < 1;
21939       
21940         var writer = this.writer;
21941         var attrs = node.attributes;
21942         // Sort attributes
21943         
21944         writer.start(node.nodeName, attrs, isEmpty, node);
21945         if (isEmpty) {
21946             return;
21947         }
21948         node = node.firstChild;
21949         if (!node) {
21950             writer.end(name);
21951             return;
21952         }
21953         while (node) {
21954             this.walk(node);
21955             node = node.nextSibling;
21956         }
21957         writer.end(name);
21958         
21959     
21960     }
21961     // Serialize element and treat all non elements as fragments
21962    
21963 }; 
21964
21965 /***
21966  * This is based loosely on tinymce 
21967  * @class Roo.htmleditor.TidyWriter
21968  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21969  *
21970  * Known issues?
21971  * - not tested much with 'PRE' formated elements.
21972  * 
21973  *
21974  *
21975  */
21976
21977 Roo.htmleditor.TidyWriter = function(settings)
21978 {
21979     
21980     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
21981     Roo.apply(this, settings);
21982     this.html = [];
21983     this.state = [];
21984      
21985     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
21986   
21987 }
21988 Roo.htmleditor.TidyWriter.prototype = {
21989
21990  
21991     state : false,
21992     
21993     indent :  '  ',
21994     
21995     // part of state...
21996     indentstr : '',
21997     in_pre: false,
21998     in_inline : false,
21999     last_inline : false,
22000     encode : false,
22001      
22002     
22003             /**
22004     * Writes the a start element such as <p id="a">.
22005     *
22006     * @method start
22007     * @param {String} name Name of the element.
22008     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22009     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22010     */
22011     start: function(name, attrs, empty, node)
22012     {
22013         var i, l, attr, value;
22014         
22015         // there are some situations where adding line break && indentation will not work. will not work.
22016         // <span / b / i ... formating?
22017         
22018         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22019         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22020         
22021         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22022         
22023         var add_lb = name == 'BR' ? false : in_inline;
22024         
22025         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22026             i_inline = false;
22027         }
22028
22029         var indentstr =  this.indentstr;
22030         
22031         // e_inline = elements that can be inline, but still allow \n before and after?
22032         // only 'BR' ??? any others?
22033         
22034         // ADD LINE BEFORE tage
22035         if (!this.in_pre) {
22036             if (in_inline) {
22037                 //code
22038                 if (name == 'BR') {
22039                     this.addLine();
22040                 } else if (this.lastElementEndsWS()) {
22041                     this.addLine();
22042                 } else{
22043                     // otherwise - no new line. (and dont indent.)
22044                     indentstr = '';
22045                 }
22046                 
22047             } else {
22048                 this.addLine();
22049             }
22050         } else {
22051             indentstr = '';
22052         }
22053         
22054         this.html.push(indentstr + '<', name.toLowerCase());
22055         
22056         if (attrs) {
22057             for (i = 0, l = attrs.length; i < l; i++) {
22058                 attr = attrs[i];
22059                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22060             }
22061         }
22062      
22063         if (empty) {
22064             if (is_short) {
22065                 this.html[this.html.length] = '/>';
22066             } else {
22067                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22068             }
22069             var e_inline = name == 'BR' ? false : this.in_inline;
22070             
22071             if (!e_inline && !this.in_pre) {
22072                 this.addLine();
22073             }
22074             return;
22075         
22076         }
22077         // not empty..
22078         this.html[this.html.length] = '>';
22079         
22080         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22081         /*
22082         if (!in_inline && !in_pre) {
22083             var cn = node.firstChild;
22084             while(cn) {
22085                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22086                     in_inline = true
22087                     break;
22088                 }
22089                 cn = cn.nextSibling;
22090             }
22091              
22092         }
22093         */
22094         
22095         
22096         this.pushState({
22097             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
22098             in_pre : in_pre,
22099             in_inline :  in_inline
22100         });
22101         // add a line after if we are not in a
22102         
22103         if (!in_inline && !in_pre) {
22104             this.addLine();
22105         }
22106         
22107             
22108          
22109         
22110     },
22111     
22112     lastElementEndsWS : function()
22113     {
22114         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22115         if (value === false) {
22116             return true;
22117         }
22118         return value.match(/\s+$/);
22119         
22120     },
22121     
22122     /**
22123      * Writes the a end element such as </p>.
22124      *
22125      * @method end
22126      * @param {String} name Name of the element.
22127      */
22128     end: function(name) {
22129         var value;
22130         this.popState();
22131         var indentstr = '';
22132         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22133         
22134         if (!this.in_pre && !in_inline) {
22135             this.addLine();
22136             indentstr  = this.indentstr;
22137         }
22138         this.html.push(indentstr + '</', name.toLowerCase(), '>');
22139         this.last_inline = in_inline;
22140         
22141         // pop the indent state..
22142     },
22143     /**
22144      * Writes a text node.
22145      *
22146      * In pre - we should not mess with the contents.
22147      * 
22148      *
22149      * @method text
22150      * @param {String} text String to write out.
22151      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22152      */
22153     text: function(text, node)
22154     {
22155         // if not in whitespace critical
22156         if (text.length < 1) {
22157             return;
22158         }
22159         if (this.in_pre) {
22160             this.html[this.html.length] =  text;
22161             return;   
22162         }
22163         
22164         if (this.in_inline) {
22165             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22166             if (text != ' ') {
22167                 text = text.replace(/\s+/,' ');  // all white space to single white space
22168                 
22169                     
22170                 // if next tag is '<BR>', then we can trim right..
22171                 if (node.nextSibling &&
22172                     node.nextSibling.nodeType == 1 &&
22173                     node.nextSibling.nodeName == 'BR' )
22174                 {
22175                     text = text.replace(/\s+$/g,'');
22176                 }
22177                 // if previous tag was a BR, we can also trim..
22178                 if (node.previousSibling &&
22179                     node.previousSibling.nodeType == 1 &&
22180                     node.previousSibling.nodeName == 'BR' )
22181                 {
22182                     text = this.indentstr +  text.replace(/^\s+/g,'');
22183                 }
22184                 if (text.match(/\n/)) {
22185                     text = text.replace(
22186                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22187                     );
22188                     // remoeve the last whitespace / line break.
22189                     text = text.replace(/\n\s+$/,'');
22190                 }
22191                 // repace long lines
22192                 
22193             }
22194              
22195             this.html[this.html.length] =  text;
22196             return;   
22197         }
22198         // see if previous element was a inline element.
22199         var indentstr = this.indentstr;
22200    
22201         text = text.replace(/\s+/g," "); // all whitespace into single white space.
22202         
22203         // should trim left?
22204         if (node.previousSibling &&
22205             node.previousSibling.nodeType == 1 &&
22206             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22207         {
22208             indentstr = '';
22209             
22210         } else {
22211             this.addLine();
22212             text = text.replace(/^\s+/,''); // trim left
22213           
22214         }
22215         // should trim right?
22216         if (node.nextSibling &&
22217             node.nextSibling.nodeType == 1 &&
22218             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22219         {
22220           // noop
22221             
22222         }  else {
22223             text = text.replace(/\s+$/,''); // trim right
22224         }
22225          
22226               
22227         
22228         
22229         
22230         if (text.length < 1) {
22231             return;
22232         }
22233         if (!text.match(/\n/)) {
22234             this.html.push(indentstr + text);
22235             return;
22236         }
22237         
22238         text = this.indentstr + text.replace(
22239             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22240         );
22241         // remoeve the last whitespace / line break.
22242         text = text.replace(/\s+$/,''); 
22243         
22244         this.html.push(text);
22245         
22246         // split and indent..
22247         
22248         
22249     },
22250     /**
22251      * Writes a cdata node such as <![CDATA[data]]>.
22252      *
22253      * @method cdata
22254      * @param {String} text String to write out inside the cdata.
22255      */
22256     cdata: function(text) {
22257         this.html.push('<![CDATA[', text, ']]>');
22258     },
22259     /**
22260     * Writes a comment node such as <!-- Comment -->.
22261     *
22262     * @method cdata
22263     * @param {String} text String to write out inside the comment.
22264     */
22265    comment: function(text) {
22266        this.html.push('<!--', text, '-->');
22267    },
22268     /**
22269      * Writes a PI node such as <?xml attr="value" ?>.
22270      *
22271      * @method pi
22272      * @param {String} name Name of the pi.
22273      * @param {String} text String to write out inside the pi.
22274      */
22275     pi: function(name, text) {
22276         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22277         this.indent != '' && this.html.push('\n');
22278     },
22279     /**
22280      * Writes a doctype node such as <!DOCTYPE data>.
22281      *
22282      * @method doctype
22283      * @param {String} text String to write out inside the doctype.
22284      */
22285     doctype: function(text) {
22286         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22287     },
22288     /**
22289      * Resets the internal buffer if one wants to reuse the writer.
22290      *
22291      * @method reset
22292      */
22293     reset: function() {
22294         this.html.length = 0;
22295         this.state = [];
22296         this.pushState({
22297             indentstr : '',
22298             in_pre : false, 
22299             in_inline : false
22300         })
22301     },
22302     /**
22303      * Returns the contents that got serialized.
22304      *
22305      * @method getContent
22306      * @return {String} HTML contents that got written down.
22307      */
22308     getContent: function() {
22309         return this.html.join('').replace(/\n$/, '');
22310     },
22311     
22312     pushState : function(cfg)
22313     {
22314         this.state.push(cfg);
22315         Roo.apply(this, cfg);
22316     },
22317     
22318     popState : function()
22319     {
22320         if (this.state.length < 1) {
22321             return; // nothing to push
22322         }
22323         var cfg = {
22324             in_pre: false,
22325             indentstr : ''
22326         };
22327         this.state.pop();
22328         if (this.state.length > 0) {
22329             cfg = this.state[this.state.length-1]; 
22330         }
22331         Roo.apply(this, cfg);
22332     },
22333     
22334     addLine: function()
22335     {
22336         if (this.html.length < 1) {
22337             return;
22338         }
22339         
22340         
22341         var value = this.html[this.html.length - 1];
22342         if (value.length > 0 && '\n' !== value) {
22343             this.html.push('\n');
22344         }
22345     }
22346     
22347     
22348 //'pre script noscript style textarea video audio iframe object code'
22349 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
22350 // inline 
22351 };
22352
22353 Roo.htmleditor.TidyWriter.inline_elements = [
22354         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22355         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
22356 ];
22357 Roo.htmleditor.TidyWriter.shortend_elements = [
22358     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22359     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22360 ];
22361
22362 Roo.htmleditor.TidyWriter.whitespace_elements = [
22363     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22364 ];/***
22365  * This is based loosely on tinymce 
22366  * @class Roo.htmleditor.TidyEntities
22367  * @static
22368  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22369  *
22370  * Not 100% sure this is actually used or needed.
22371  */
22372
22373 Roo.htmleditor.TidyEntities = {
22374     
22375     /**
22376      * initialize data..
22377      */
22378     init : function (){
22379      
22380         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22381        
22382     },
22383
22384
22385     buildEntitiesLookup: function(items, radix) {
22386         var i, chr, entity, lookup = {};
22387         if (!items) {
22388             return {};
22389         }
22390         items = typeof(items) == 'string' ? items.split(',') : items;
22391         radix = radix || 10;
22392         // Build entities lookup table
22393         for (i = 0; i < items.length; i += 2) {
22394             chr = String.fromCharCode(parseInt(items[i], radix));
22395             // Only add non base entities
22396             if (!this.baseEntities[chr]) {
22397                 entity = '&' + items[i + 1] + ';';
22398                 lookup[chr] = entity;
22399                 lookup[entity] = chr;
22400             }
22401         }
22402         return lookup;
22403         
22404     },
22405     
22406     asciiMap : {
22407             128: '€',
22408             130: '‚',
22409             131: 'ƒ',
22410             132: '„',
22411             133: '…',
22412             134: '†',
22413             135: '‡',
22414             136: 'ˆ',
22415             137: '‰',
22416             138: 'Š',
22417             139: '‹',
22418             140: 'Œ',
22419             142: 'Ž',
22420             145: '‘',
22421             146: '’',
22422             147: '“',
22423             148: '”',
22424             149: '•',
22425             150: '–',
22426             151: '—',
22427             152: '˜',
22428             153: '™',
22429             154: 'š',
22430             155: '›',
22431             156: 'œ',
22432             158: 'ž',
22433             159: 'Ÿ'
22434     },
22435     // Raw entities
22436     baseEntities : {
22437         '"': '&quot;',
22438         // Needs to be escaped since the YUI compressor would otherwise break the code
22439         '\'': '&#39;',
22440         '<': '&lt;',
22441         '>': '&gt;',
22442         '&': '&amp;',
22443         '`': '&#96;'
22444     },
22445     // Reverse lookup table for raw entities
22446     reverseEntities : {
22447         '&lt;': '<',
22448         '&gt;': '>',
22449         '&amp;': '&',
22450         '&quot;': '"',
22451         '&apos;': '\''
22452     },
22453     
22454     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22455     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22456     rawCharsRegExp : /[<>&\"\']/g,
22457     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22458     namedEntities  : false,
22459     namedEntitiesData : [ 
22460         '50',
22461         'nbsp',
22462         '51',
22463         'iexcl',
22464         '52',
22465         'cent',
22466         '53',
22467         'pound',
22468         '54',
22469         'curren',
22470         '55',
22471         'yen',
22472         '56',
22473         'brvbar',
22474         '57',
22475         'sect',
22476         '58',
22477         'uml',
22478         '59',
22479         'copy',
22480         '5a',
22481         'ordf',
22482         '5b',
22483         'laquo',
22484         '5c',
22485         'not',
22486         '5d',
22487         'shy',
22488         '5e',
22489         'reg',
22490         '5f',
22491         'macr',
22492         '5g',
22493         'deg',
22494         '5h',
22495         'plusmn',
22496         '5i',
22497         'sup2',
22498         '5j',
22499         'sup3',
22500         '5k',
22501         'acute',
22502         '5l',
22503         'micro',
22504         '5m',
22505         'para',
22506         '5n',
22507         'middot',
22508         '5o',
22509         'cedil',
22510         '5p',
22511         'sup1',
22512         '5q',
22513         'ordm',
22514         '5r',
22515         'raquo',
22516         '5s',
22517         'frac14',
22518         '5t',
22519         'frac12',
22520         '5u',
22521         'frac34',
22522         '5v',
22523         'iquest',
22524         '60',
22525         'Agrave',
22526         '61',
22527         'Aacute',
22528         '62',
22529         'Acirc',
22530         '63',
22531         'Atilde',
22532         '64',
22533         'Auml',
22534         '65',
22535         'Aring',
22536         '66',
22537         'AElig',
22538         '67',
22539         'Ccedil',
22540         '68',
22541         'Egrave',
22542         '69',
22543         'Eacute',
22544         '6a',
22545         'Ecirc',
22546         '6b',
22547         'Euml',
22548         '6c',
22549         'Igrave',
22550         '6d',
22551         'Iacute',
22552         '6e',
22553         'Icirc',
22554         '6f',
22555         'Iuml',
22556         '6g',
22557         'ETH',
22558         '6h',
22559         'Ntilde',
22560         '6i',
22561         'Ograve',
22562         '6j',
22563         'Oacute',
22564         '6k',
22565         'Ocirc',
22566         '6l',
22567         'Otilde',
22568         '6m',
22569         'Ouml',
22570         '6n',
22571         'times',
22572         '6o',
22573         'Oslash',
22574         '6p',
22575         'Ugrave',
22576         '6q',
22577         'Uacute',
22578         '6r',
22579         'Ucirc',
22580         '6s',
22581         'Uuml',
22582         '6t',
22583         'Yacute',
22584         '6u',
22585         'THORN',
22586         '6v',
22587         'szlig',
22588         '70',
22589         'agrave',
22590         '71',
22591         'aacute',
22592         '72',
22593         'acirc',
22594         '73',
22595         'atilde',
22596         '74',
22597         'auml',
22598         '75',
22599         'aring',
22600         '76',
22601         'aelig',
22602         '77',
22603         'ccedil',
22604         '78',
22605         'egrave',
22606         '79',
22607         'eacute',
22608         '7a',
22609         'ecirc',
22610         '7b',
22611         'euml',
22612         '7c',
22613         'igrave',
22614         '7d',
22615         'iacute',
22616         '7e',
22617         'icirc',
22618         '7f',
22619         'iuml',
22620         '7g',
22621         'eth',
22622         '7h',
22623         'ntilde',
22624         '7i',
22625         'ograve',
22626         '7j',
22627         'oacute',
22628         '7k',
22629         'ocirc',
22630         '7l',
22631         'otilde',
22632         '7m',
22633         'ouml',
22634         '7n',
22635         'divide',
22636         '7o',
22637         'oslash',
22638         '7p',
22639         'ugrave',
22640         '7q',
22641         'uacute',
22642         '7r',
22643         'ucirc',
22644         '7s',
22645         'uuml',
22646         '7t',
22647         'yacute',
22648         '7u',
22649         'thorn',
22650         '7v',
22651         'yuml',
22652         'ci',
22653         'fnof',
22654         'sh',
22655         'Alpha',
22656         'si',
22657         'Beta',
22658         'sj',
22659         'Gamma',
22660         'sk',
22661         'Delta',
22662         'sl',
22663         'Epsilon',
22664         'sm',
22665         'Zeta',
22666         'sn',
22667         'Eta',
22668         'so',
22669         'Theta',
22670         'sp',
22671         'Iota',
22672         'sq',
22673         'Kappa',
22674         'sr',
22675         'Lambda',
22676         'ss',
22677         'Mu',
22678         'st',
22679         'Nu',
22680         'su',
22681         'Xi',
22682         'sv',
22683         'Omicron',
22684         't0',
22685         'Pi',
22686         't1',
22687         'Rho',
22688         't3',
22689         'Sigma',
22690         't4',
22691         'Tau',
22692         't5',
22693         'Upsilon',
22694         't6',
22695         'Phi',
22696         't7',
22697         'Chi',
22698         't8',
22699         'Psi',
22700         't9',
22701         'Omega',
22702         'th',
22703         'alpha',
22704         'ti',
22705         'beta',
22706         'tj',
22707         'gamma',
22708         'tk',
22709         'delta',
22710         'tl',
22711         'epsilon',
22712         'tm',
22713         'zeta',
22714         'tn',
22715         'eta',
22716         'to',
22717         'theta',
22718         'tp',
22719         'iota',
22720         'tq',
22721         'kappa',
22722         'tr',
22723         'lambda',
22724         'ts',
22725         'mu',
22726         'tt',
22727         'nu',
22728         'tu',
22729         'xi',
22730         'tv',
22731         'omicron',
22732         'u0',
22733         'pi',
22734         'u1',
22735         'rho',
22736         'u2',
22737         'sigmaf',
22738         'u3',
22739         'sigma',
22740         'u4',
22741         'tau',
22742         'u5',
22743         'upsilon',
22744         'u6',
22745         'phi',
22746         'u7',
22747         'chi',
22748         'u8',
22749         'psi',
22750         'u9',
22751         'omega',
22752         'uh',
22753         'thetasym',
22754         'ui',
22755         'upsih',
22756         'um',
22757         'piv',
22758         '812',
22759         'bull',
22760         '816',
22761         'hellip',
22762         '81i',
22763         'prime',
22764         '81j',
22765         'Prime',
22766         '81u',
22767         'oline',
22768         '824',
22769         'frasl',
22770         '88o',
22771         'weierp',
22772         '88h',
22773         'image',
22774         '88s',
22775         'real',
22776         '892',
22777         'trade',
22778         '89l',
22779         'alefsym',
22780         '8cg',
22781         'larr',
22782         '8ch',
22783         'uarr',
22784         '8ci',
22785         'rarr',
22786         '8cj',
22787         'darr',
22788         '8ck',
22789         'harr',
22790         '8dl',
22791         'crarr',
22792         '8eg',
22793         'lArr',
22794         '8eh',
22795         'uArr',
22796         '8ei',
22797         'rArr',
22798         '8ej',
22799         'dArr',
22800         '8ek',
22801         'hArr',
22802         '8g0',
22803         'forall',
22804         '8g2',
22805         'part',
22806         '8g3',
22807         'exist',
22808         '8g5',
22809         'empty',
22810         '8g7',
22811         'nabla',
22812         '8g8',
22813         'isin',
22814         '8g9',
22815         'notin',
22816         '8gb',
22817         'ni',
22818         '8gf',
22819         'prod',
22820         '8gh',
22821         'sum',
22822         '8gi',
22823         'minus',
22824         '8gn',
22825         'lowast',
22826         '8gq',
22827         'radic',
22828         '8gt',
22829         'prop',
22830         '8gu',
22831         'infin',
22832         '8h0',
22833         'ang',
22834         '8h7',
22835         'and',
22836         '8h8',
22837         'or',
22838         '8h9',
22839         'cap',
22840         '8ha',
22841         'cup',
22842         '8hb',
22843         'int',
22844         '8hk',
22845         'there4',
22846         '8hs',
22847         'sim',
22848         '8i5',
22849         'cong',
22850         '8i8',
22851         'asymp',
22852         '8j0',
22853         'ne',
22854         '8j1',
22855         'equiv',
22856         '8j4',
22857         'le',
22858         '8j5',
22859         'ge',
22860         '8k2',
22861         'sub',
22862         '8k3',
22863         'sup',
22864         '8k4',
22865         'nsub',
22866         '8k6',
22867         'sube',
22868         '8k7',
22869         'supe',
22870         '8kl',
22871         'oplus',
22872         '8kn',
22873         'otimes',
22874         '8l5',
22875         'perp',
22876         '8m5',
22877         'sdot',
22878         '8o8',
22879         'lceil',
22880         '8o9',
22881         'rceil',
22882         '8oa',
22883         'lfloor',
22884         '8ob',
22885         'rfloor',
22886         '8p9',
22887         'lang',
22888         '8pa',
22889         'rang',
22890         '9ea',
22891         'loz',
22892         '9j0',
22893         'spades',
22894         '9j3',
22895         'clubs',
22896         '9j5',
22897         'hearts',
22898         '9j6',
22899         'diams',
22900         'ai',
22901         'OElig',
22902         'aj',
22903         'oelig',
22904         'b0',
22905         'Scaron',
22906         'b1',
22907         'scaron',
22908         'bo',
22909         'Yuml',
22910         'm6',
22911         'circ',
22912         'ms',
22913         'tilde',
22914         '802',
22915         'ensp',
22916         '803',
22917         'emsp',
22918         '809',
22919         'thinsp',
22920         '80c',
22921         'zwnj',
22922         '80d',
22923         'zwj',
22924         '80e',
22925         'lrm',
22926         '80f',
22927         'rlm',
22928         '80j',
22929         'ndash',
22930         '80k',
22931         'mdash',
22932         '80o',
22933         'lsquo',
22934         '80p',
22935         'rsquo',
22936         '80q',
22937         'sbquo',
22938         '80s',
22939         'ldquo',
22940         '80t',
22941         'rdquo',
22942         '80u',
22943         'bdquo',
22944         '810',
22945         'dagger',
22946         '811',
22947         'Dagger',
22948         '81g',
22949         'permil',
22950         '81p',
22951         'lsaquo',
22952         '81q',
22953         'rsaquo',
22954         '85c',
22955         'euro'
22956     ],
22957
22958          
22959     /**
22960      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
22961      *
22962      * @method encodeRaw
22963      * @param {String} text Text to encode.
22964      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22965      * @return {String} Entity encoded text.
22966      */
22967     encodeRaw: function(text, attr)
22968     {
22969         var t = this;
22970         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
22971             return t.baseEntities[chr] || chr;
22972         });
22973     },
22974     /**
22975      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
22976      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
22977      * and is exposed as the DOMUtils.encode function.
22978      *
22979      * @method encodeAllRaw
22980      * @param {String} text Text to encode.
22981      * @return {String} Entity encoded text.
22982      */
22983     encodeAllRaw: function(text) {
22984         var t = this;
22985         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
22986             return t.baseEntities[chr] || chr;
22987         });
22988     },
22989     /**
22990      * Encodes the specified string using numeric entities. The core entities will be
22991      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
22992      *
22993      * @method encodeNumeric
22994      * @param {String} text Text to encode.
22995      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22996      * @return {String} Entity encoded text.
22997      */
22998     encodeNumeric: function(text, attr) {
22999         var t = this;
23000         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23001             // Multi byte sequence convert it to a single entity
23002             if (chr.length > 1) {
23003                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
23004             }
23005             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23006         });
23007     },
23008     /**
23009      * Encodes the specified string using named entities. The core entities will be encoded
23010      * as named ones but all non lower ascii characters will be encoded into named entities.
23011      *
23012      * @method encodeNamed
23013      * @param {String} text Text to encode.
23014      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23015      * @param {Object} entities Optional parameter with entities to use.
23016      * @return {String} Entity encoded text.
23017      */
23018     encodeNamed: function(text, attr, entities) {
23019         var t = this;
23020         entities = entities || this.namedEntities;
23021         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23022             return t.baseEntities[chr] || entities[chr] || chr;
23023         });
23024     },
23025     /**
23026      * Returns an encode function based on the name(s) and it's optional entities.
23027      *
23028      * @method getEncodeFunc
23029      * @param {String} name Comma separated list of encoders for example named,numeric.
23030      * @param {String} entities Optional parameter with entities to use instead of the built in set.
23031      * @return {function} Encode function to be used.
23032      */
23033     getEncodeFunc: function(name, entities) {
23034         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23035         var t = this;
23036         function encodeNamedAndNumeric(text, attr) {
23037             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23038                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23039             });
23040         }
23041
23042         function encodeCustomNamed(text, attr) {
23043             return t.encodeNamed(text, attr, entities);
23044         }
23045         // Replace + with , to be compatible with previous TinyMCE versions
23046         name = this.makeMap(name.replace(/\+/g, ','));
23047         // Named and numeric encoder
23048         if (name.named && name.numeric) {
23049             return this.encodeNamedAndNumeric;
23050         }
23051         // Named encoder
23052         if (name.named) {
23053             // Custom names
23054             if (entities) {
23055                 return encodeCustomNamed;
23056             }
23057             return this.encodeNamed;
23058         }
23059         // Numeric
23060         if (name.numeric) {
23061             return this.encodeNumeric;
23062         }
23063         // Raw encoder
23064         return this.encodeRaw;
23065     },
23066     /**
23067      * Decodes the specified string, this will replace entities with raw UTF characters.
23068      *
23069      * @method decode
23070      * @param {String} text Text to entity decode.
23071      * @return {String} Entity decoded string.
23072      */
23073     decode: function(text)
23074     {
23075         var  t = this;
23076         return text.replace(this.entityRegExp, function(all, numeric) {
23077             if (numeric) {
23078                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23079                 // Support upper UTF
23080                 if (numeric > 65535) {
23081                     numeric -= 65536;
23082                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23083                 }
23084                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23085             }
23086             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23087         });
23088     },
23089     nativeDecode : function (text) {
23090         return text;
23091     },
23092     makeMap : function (items, delim, map) {
23093                 var i;
23094                 items = items || [];
23095                 delim = delim || ',';
23096                 if (typeof items == "string") {
23097                         items = items.split(delim);
23098                 }
23099                 map = map || {};
23100                 i = items.length;
23101                 while (i--) {
23102                         map[items[i]] = {};
23103                 }
23104                 return map;
23105         }
23106 };
23107     
23108     
23109     
23110 Roo.htmleditor.TidyEntities.init();
23111 /**
23112  * @class Roo.htmleditor.KeyEnter
23113  * Handle Enter press..
23114  * @cfg {Roo.HtmlEditorCore} core the editor.
23115  * @constructor
23116  * Create a new Filter.
23117  * @param {Object} config Configuration options
23118  */
23119
23120
23121
23122
23123
23124 Roo.htmleditor.KeyEnter = function(cfg) {
23125     Roo.apply(this, cfg);
23126     // this does not actually call walk as it's really just a abstract class
23127  
23128     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23129 }
23130
23131 //Roo.htmleditor.KeyEnter.i = 0;
23132
23133
23134 Roo.htmleditor.KeyEnter.prototype = {
23135     
23136     core : false,
23137     
23138     keypress : function(e)
23139     {
23140         if (e.charCode != 13 && e.charCode != 10) {
23141             Roo.log([e.charCode,e]);
23142             return true;
23143         }
23144         e.preventDefault();
23145         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23146         var doc = this.core.doc;
23147           //add a new line
23148        
23149     
23150         var sel = this.core.getSelection();
23151         var range = sel.getRangeAt(0);
23152         var n = range.commonAncestorContainer;
23153         var pc = range.closest([ 'ol', 'ul']);
23154         var pli = range.closest('li');
23155         if (!pc || e.ctrlKey) {
23156             sel.insertNode('br', 'after'); 
23157          
23158             this.core.undoManager.addEvent();
23159             this.core.fireEditorEvent(e);
23160             return false;
23161         }
23162         
23163         // deal with <li> insetion
23164         if (pli.innerText.trim() == '' &&
23165             pli.previousSibling &&
23166             pli.previousSibling.nodeName == 'LI' &&
23167             pli.previousSibling.innerText.trim() ==  '') {
23168             pli.parentNode.removeChild(pli.previousSibling);
23169             sel.cursorAfter(pc);
23170             this.core.undoManager.addEvent();
23171             this.core.fireEditorEvent(e);
23172             return false;
23173         }
23174     
23175         var li = doc.createElement('LI');
23176         li.innerHTML = '&nbsp;';
23177         if (!pli || !pli.firstSibling) {
23178             pc.appendChild(li);
23179         } else {
23180             pli.parentNode.insertBefore(li, pli.firstSibling);
23181         }
23182         sel.cursorText (li.firstChild);
23183       
23184         this.core.undoManager.addEvent();
23185         this.core.fireEditorEvent(e);
23186
23187         return false;
23188         
23189     
23190         
23191         
23192          
23193     }
23194 };
23195      
23196 /**
23197  * @class Roo.htmleditor.Block
23198  * Base class for html editor blocks - do not use it directly .. extend it..
23199  * @cfg {DomElement} node The node to apply stuff to.
23200  * @cfg {String} friendly_name the name that appears in the context bar about this block
23201  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23202  
23203  * @constructor
23204  * Create a new Filter.
23205  * @param {Object} config Configuration options
23206  */
23207
23208 Roo.htmleditor.Block  = function(cfg)
23209 {
23210     // do nothing .. should not be called really.
23211 }
23212 /**
23213  * factory method to get the block from an element (using cache if necessary)
23214  * @static
23215  * @param {HtmlElement} the dom element
23216  */
23217 Roo.htmleditor.Block.factory = function(node)
23218 {
23219     var cc = Roo.htmleditor.Block.cache;
23220     var id = Roo.get(node).id;
23221     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23222         Roo.htmleditor.Block.cache[id].readElement(node);
23223         return Roo.htmleditor.Block.cache[id];
23224     }
23225     var db  = node.getAttribute('data-block');
23226     if (!db) {
23227         db = node.nodeName.toLowerCase().toUpperCaseFirst();
23228     }
23229     var cls = Roo.htmleditor['Block' + db];
23230     if (typeof(cls) == 'undefined') {
23231         //Roo.log(node.getAttribute('data-block'));
23232         Roo.log("OOps missing block : " + 'Block' + db);
23233         return false;
23234     }
23235     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23236     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
23237 };
23238
23239 /**
23240  * initalize all Elements from content that are 'blockable'
23241  * @static
23242  * @param the body element
23243  */
23244 Roo.htmleditor.Block.initAll = function(body, type)
23245 {
23246     if (typeof(type) == 'undefined') {
23247         var ia = Roo.htmleditor.Block.initAll;
23248         ia(body,'table');
23249         ia(body,'td');
23250         ia(body,'figure');
23251         return;
23252     }
23253     Roo.each(Roo.get(body).query(type), function(e) {
23254         Roo.htmleditor.Block.factory(e);    
23255     },this);
23256 };
23257 // question goes here... do we need to clear out this cache sometimes?
23258 // or show we make it relivant to the htmleditor.
23259 Roo.htmleditor.Block.cache = {};
23260
23261 Roo.htmleditor.Block.prototype = {
23262     
23263     node : false,
23264     
23265      // used by context menu
23266     friendly_name : 'Based Block',
23267     
23268     // text for button to delete this element
23269     deleteTitle : false,
23270     
23271     context : false,
23272     /**
23273      * Update a node with values from this object
23274      * @param {DomElement} node
23275      */
23276     updateElement : function(node)
23277     {
23278         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23279     },
23280      /**
23281      * convert to plain HTML for calling insertAtCursor..
23282      */
23283     toHTML : function()
23284     {
23285         return Roo.DomHelper.markup(this.toObject());
23286     },
23287     /**
23288      * used by readEleemnt to extract data from a node
23289      * may need improving as it's pretty basic
23290      
23291      * @param {DomElement} node
23292      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23293      * @param {String} attribute (use html - for contents, or style for using next param as style)
23294      * @param {String} style the style property - eg. text-align
23295      */
23296     getVal : function(node, tag, attr, style)
23297     {
23298         var n = node;
23299         if (tag !== true && n.tagName != tag.toUpperCase()) {
23300             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23301             // but kiss for now.
23302             n = node.getElementsByTagName(tag).item(0);
23303         }
23304         if (!n) {
23305             return '';
23306         }
23307         if (attr == 'html') {
23308             return n.innerHTML;
23309         }
23310         if (attr == 'style') {
23311             return n.style[style]; 
23312         }
23313         
23314         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23315             
23316     },
23317     /**
23318      * create a DomHelper friendly object - for use with 
23319      * Roo.DomHelper.markup / overwrite / etc..
23320      * (override this)
23321      */
23322     toObject : function()
23323     {
23324         return {};
23325     },
23326       /**
23327      * Read a node that has a 'data-block' property - and extract the values from it.
23328      * @param {DomElement} node - the node
23329      */
23330     readElement : function(node)
23331     {
23332         
23333     } 
23334     
23335     
23336 };
23337
23338  
23339
23340 /**
23341  * @class Roo.htmleditor.BlockFigure
23342  * Block that has an image and a figcaption
23343  * @cfg {String} image_src the url for the image
23344  * @cfg {String} align (left|right) alignment for the block default left
23345  * @cfg {String} caption the text to appear below  (and in the alt tag)
23346  * @cfg {String} caption_display (block|none) display or not the caption
23347  * @cfg {String|number} image_width the width of the image number or %?
23348  * @cfg {String|number} image_height the height of the image number or %?
23349  * 
23350  * @constructor
23351  * Create a new Filter.
23352  * @param {Object} config Configuration options
23353  */
23354
23355 Roo.htmleditor.BlockFigure = function(cfg)
23356 {
23357     if (cfg.node) {
23358         this.readElement(cfg.node);
23359         this.updateElement(cfg.node);
23360     }
23361     Roo.apply(this, cfg);
23362 }
23363 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23364  
23365     
23366     // setable values.
23367     image_src: '',
23368     align: 'center',
23369     caption : '',
23370     caption_display : 'block',
23371     width : '100%',
23372     cls : '',
23373     href: '',
23374     video_url : '',
23375     
23376     // margin: '2%', not used
23377     
23378     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
23379
23380     
23381     // used by context menu
23382     friendly_name : 'Image with caption',
23383     deleteTitle : "Delete Image and Caption",
23384     
23385     contextMenu : function(toolbar)
23386     {
23387         
23388         var block = function() {
23389             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23390         };
23391         
23392         
23393         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23394         
23395         var syncValue = toolbar.editorcore.syncValue;
23396         
23397         var fields = {};
23398         
23399         return [
23400              {
23401                 xtype : 'TextItem',
23402                 text : "Source: ",
23403                 xns : rooui.Toolbar  //Boostrap?
23404             },
23405             {
23406                 xtype : 'Button',
23407                 text: 'Change Image URL',
23408                  
23409                 listeners : {
23410                     click: function (btn, state)
23411                     {
23412                         var b = block();
23413                         
23414                         Roo.MessageBox.show({
23415                             title : "Image Source URL",
23416                             msg : "Enter the url for the image",
23417                             buttons: Roo.MessageBox.OKCANCEL,
23418                             fn: function(btn, val){
23419                                 if (btn != 'ok') {
23420                                     return;
23421                                 }
23422                                 b.image_src = val;
23423                                 b.updateElement();
23424                                 syncValue();
23425                                 toolbar.editorcore.onEditorEvent();
23426                             },
23427                             minWidth:250,
23428                             prompt:true,
23429                             //multiline: multiline,
23430                             modal : true,
23431                             value : b.image_src
23432                         });
23433                     }
23434                 },
23435                 xns : rooui.Toolbar
23436             },
23437          
23438             {
23439                 xtype : 'Button',
23440                 text: 'Change Link URL',
23441                  
23442                 listeners : {
23443                     click: function (btn, state)
23444                     {
23445                         var b = block();
23446                         
23447                         Roo.MessageBox.show({
23448                             title : "Link URL",
23449                             msg : "Enter the url for the link - leave blank to have no link",
23450                             buttons: Roo.MessageBox.OKCANCEL,
23451                             fn: function(btn, val){
23452                                 if (btn != 'ok') {
23453                                     return;
23454                                 }
23455                                 b.href = val;
23456                                 b.updateElement();
23457                                 syncValue();
23458                                 toolbar.editorcore.onEditorEvent();
23459                             },
23460                             minWidth:250,
23461                             prompt:true,
23462                             //multiline: multiline,
23463                             modal : true,
23464                             value : b.href
23465                         });
23466                     }
23467                 },
23468                 xns : rooui.Toolbar
23469             },
23470             {
23471                 xtype : 'Button',
23472                 text: 'Show Video URL',
23473                  
23474                 listeners : {
23475                     click: function (btn, state)
23476                     {
23477                         Roo.MessageBox.alert("Video URL",
23478                             block().video_url == '' ? 'This image is not linked ot a video' :
23479                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23480                     }
23481                 },
23482                 xns : rooui.Toolbar
23483             },
23484             
23485             
23486             {
23487                 xtype : 'TextItem',
23488                 text : "Width: ",
23489                 xns : rooui.Toolbar  //Boostrap?
23490             },
23491             {
23492                 xtype : 'ComboBox',
23493                 allowBlank : false,
23494                 displayField : 'val',
23495                 editable : true,
23496                 listWidth : 100,
23497                 triggerAction : 'all',
23498                 typeAhead : true,
23499                 valueField : 'val',
23500                 width : 70,
23501                 name : 'width',
23502                 listeners : {
23503                     select : function (combo, r, index)
23504                     {
23505                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23506                         var b = block();
23507                         b.width = r.get('val');
23508                         b.updateElement();
23509                         syncValue();
23510                         toolbar.editorcore.onEditorEvent();
23511                     }
23512                 },
23513                 xns : rooui.form,
23514                 store : {
23515                     xtype : 'SimpleStore',
23516                     data : [
23517                         ['auto'],
23518                         ['50%'],
23519                         ['100%']
23520                     ],
23521                     fields : [ 'val'],
23522                     xns : Roo.data
23523                 }
23524             },
23525             {
23526                 xtype : 'TextItem',
23527                 text : "Align: ",
23528                 xns : rooui.Toolbar  //Boostrap?
23529             },
23530             {
23531                 xtype : 'ComboBox',
23532                 allowBlank : false,
23533                 displayField : 'val',
23534                 editable : true,
23535                 listWidth : 100,
23536                 triggerAction : 'all',
23537                 typeAhead : true,
23538                 valueField : 'val',
23539                 width : 70,
23540                 name : 'align',
23541                 listeners : {
23542                     select : function (combo, r, index)
23543                     {
23544                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23545                         var b = block();
23546                         b.align = r.get('val');
23547                         b.updateElement();
23548                         syncValue();
23549                         toolbar.editorcore.onEditorEvent();
23550                     }
23551                 },
23552                 xns : rooui.form,
23553                 store : {
23554                     xtype : 'SimpleStore',
23555                     data : [
23556                         ['left'],
23557                         ['right'],
23558                         ['center']
23559                     ],
23560                     fields : [ 'val'],
23561                     xns : Roo.data
23562                 }
23563             },
23564             
23565             
23566             {
23567                 xtype : 'Button',
23568                 text: 'Hide Caption',
23569                 name : 'caption_display',
23570                 pressed : false,
23571                 enableToggle : true,
23572                 setValue : function(v) {
23573                     this.toggle(v == 'block' ? false : true);
23574                 },
23575                 listeners : {
23576                     toggle: function (btn, state)
23577                     {
23578                         var b  = block();
23579                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
23580                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23581                         b.updateElement();
23582                         syncValue();
23583                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23584                         toolbar.editorcore.onEditorEvent();
23585                     }
23586                 },
23587                 xns : rooui.Toolbar
23588             }
23589         ];
23590         
23591     },
23592     /**
23593      * create a DomHelper friendly object - for use with
23594      * Roo.DomHelper.markup / overwrite / etc..
23595      */
23596     toObject : function()
23597     {
23598         var d = document.createElement('div');
23599         d.innerHTML = this.caption;
23600         
23601         var m = this.width == '50%' && this.align == 'center' ? '0 auto' : 0; 
23602         
23603         var img =   {
23604             tag : 'img',
23605             contenteditable : 'false',
23606             src : this.image_src,
23607             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
23608             style: {
23609                 width : 'auto',
23610                 'max-width': '100%',
23611                 margin : '0px' 
23612                 
23613                 
23614             }
23615         };
23616         /*
23617         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
23618                     '<a href="{2}">' + 
23619                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
23620                     '</a>' + 
23621                 '</div>',
23622         */
23623                 
23624         if (this.href.length > 0) {
23625             img = {
23626                 tag : 'a',
23627                 href: this.href,
23628                 contenteditable : 'true',
23629                 cn : [
23630                     img
23631                 ]
23632             };
23633         }
23634         
23635         
23636         if (this.video_url.length > 0) {
23637             img = {
23638                 tag : 'div',
23639                 cls : this.cls,
23640                 frameborder : 0,
23641                 allowfullscreen : true,
23642                 width : 420,  // these are for video tricks - that we replace the outer
23643                 height : 315,
23644                 src : this.video_url,
23645                 cn : [
23646                     img
23647                 ]
23648             };
23649         }
23650         
23651         var captionhtml = this.caption_display == 'hidden' ? this.caption : (this.caption.length ? this.caption : "Caption");
23652         
23653         return  {
23654             tag: 'figure',
23655             'data-block' : 'Figure',
23656             contenteditable : 'false',
23657             style : {
23658                 display: 'block',
23659                 float :  this.align ,
23660                 'max-width':  this.width,
23661                 width : 'auto',
23662                 margin:  m,
23663                 padding: '10px'
23664                 
23665             },
23666            
23667             
23668             align : this.align,
23669             cn : [
23670                 img,
23671               
23672                 {
23673                     tag: 'figcaption',
23674                     
23675                     style : {
23676                         'text-align': 'left',
23677                         'margin-top' : '16px',
23678                         'font-size' : '16px',
23679                         'line-height' : '24px',
23680                          display : this.caption_display
23681                     },
23682                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
23683                     cn : [
23684                         {
23685                             // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
23686                             tag : 'i',
23687                             contenteditable : true,
23688                             html : captionhtml
23689                         }
23690                     ]
23691                     
23692                 }
23693             ]
23694         };
23695          
23696     },
23697     
23698     readElement : function(node)
23699     {
23700         // this should not really come from the link...
23701         this.video_url = this.getVal(node, 'div', 'src');
23702         this.cls = this.getVal(node, 'div', 'class');
23703         this.href = this.getVal(node, 'a', 'href');
23704         
23705         this.image_src = this.getVal(node, 'img', 'src');
23706          
23707         this.align = this.getVal(node, 'figure', 'align');
23708         this.caption = this.getVal(node, 'figcaption', 'html');
23709         // remove '<i>
23710         if (this.caption.trim().match(/^<i[^>]*>/i)) {
23711             this.caption = this.caption.trim().replace(/^<i[^>]*>/i, '').replace(/^<\/i>$/i, '');
23712         }
23713         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
23714         this.width = this.getVal(node, 'figure', 'style', 'max-width');
23715         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
23716         
23717     },
23718     removeNode : function()
23719     {
23720         return this.node;
23721     }
23722     
23723   
23724    
23725      
23726     
23727     
23728     
23729     
23730 })
23731
23732  
23733
23734 /**
23735  * @class Roo.htmleditor.BlockTable
23736  * Block that manages a table
23737  * 
23738  * @constructor
23739  * Create a new Filter.
23740  * @param {Object} config Configuration options
23741  */
23742
23743 Roo.htmleditor.BlockTable = function(cfg)
23744 {
23745     if (cfg.node) {
23746         this.readElement(cfg.node);
23747         this.updateElement(cfg.node);
23748     }
23749     Roo.apply(this, cfg);
23750     if (!cfg.node) {
23751         this.rows = [];
23752         for(var r = 0; r < this.no_row; r++) {
23753             this.rows[r] = [];
23754             for(var c = 0; c < this.no_col; c++) {
23755                 this.rows[r][c] = this.emptyCell();
23756             }
23757         }
23758     }
23759     
23760     
23761 }
23762 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
23763  
23764     rows : false,
23765     no_col : 1,
23766     no_row : 1,
23767     
23768     
23769     width: '100%',
23770     
23771     // used by context menu
23772     friendly_name : 'Table',
23773     deleteTitle : 'Delete Table',
23774     // context menu is drawn once..
23775     
23776     contextMenu : function(toolbar)
23777     {
23778         
23779         var block = function() {
23780             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23781         };
23782         
23783         
23784         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23785         
23786         var syncValue = toolbar.editorcore.syncValue;
23787         
23788         var fields = {};
23789         
23790         return [
23791             {
23792                 xtype : 'TextItem',
23793                 text : "Width: ",
23794                 xns : rooui.Toolbar  //Boostrap?
23795             },
23796             {
23797                 xtype : 'ComboBox',
23798                 allowBlank : false,
23799                 displayField : 'val',
23800                 editable : true,
23801                 listWidth : 100,
23802                 triggerAction : 'all',
23803                 typeAhead : true,
23804                 valueField : 'val',
23805                 width : 100,
23806                 name : 'width',
23807                 listeners : {
23808                     select : function (combo, r, index)
23809                     {
23810                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23811                         var b = block();
23812                         b.width = r.get('val');
23813                         b.updateElement();
23814                         syncValue();
23815                         toolbar.editorcore.onEditorEvent();
23816                     }
23817                 },
23818                 xns : rooui.form,
23819                 store : {
23820                     xtype : 'SimpleStore',
23821                     data : [
23822                         ['100%'],
23823                         ['auto']
23824                     ],
23825                     fields : [ 'val'],
23826                     xns : Roo.data
23827                 }
23828             },
23829             // -------- Cols
23830             
23831             {
23832                 xtype : 'TextItem',
23833                 text : "Columns: ",
23834                 xns : rooui.Toolbar  //Boostrap?
23835             },
23836          
23837             {
23838                 xtype : 'Button',
23839                 text: '-',
23840                 listeners : {
23841                     click : function (_self, e)
23842                     {
23843                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23844                         block().removeColumn();
23845                         syncValue();
23846                         toolbar.editorcore.onEditorEvent();
23847                     }
23848                 },
23849                 xns : rooui.Toolbar
23850             },
23851             {
23852                 xtype : 'Button',
23853                 text: '+',
23854                 listeners : {
23855                     click : function (_self, e)
23856                     {
23857                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23858                         block().addColumn();
23859                         syncValue();
23860                         toolbar.editorcore.onEditorEvent();
23861                     }
23862                 },
23863                 xns : rooui.Toolbar
23864             },
23865             // -------- ROWS
23866             {
23867                 xtype : 'TextItem',
23868                 text : "Rows: ",
23869                 xns : rooui.Toolbar  //Boostrap?
23870             },
23871          
23872             {
23873                 xtype : 'Button',
23874                 text: '-',
23875                 listeners : {
23876                     click : function (_self, e)
23877                     {
23878                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23879                         block().removeRow();
23880                         syncValue();
23881                         toolbar.editorcore.onEditorEvent();
23882                     }
23883                 },
23884                 xns : rooui.Toolbar
23885             },
23886             {
23887                 xtype : 'Button',
23888                 text: '+',
23889                 listeners : {
23890                     click : function (_self, e)
23891                     {
23892                         block().addRow();
23893                         syncValue();
23894                         toolbar.editorcore.onEditorEvent();
23895                     }
23896                 },
23897                 xns : rooui.Toolbar
23898             },
23899             // -------- ROWS
23900             {
23901                 xtype : 'Button',
23902                 text: 'Reset Column Widths',
23903                 listeners : {
23904                     
23905                     click : function (_self, e)
23906                     {
23907                         block().resetWidths();
23908                         syncValue();
23909                         toolbar.editorcore.onEditorEvent();
23910                     }
23911                 },
23912                 xns : rooui.Toolbar
23913             } 
23914             
23915             
23916             
23917         ];
23918         
23919     },
23920     
23921     
23922   /**
23923      * create a DomHelper friendly object - for use with
23924      * Roo.DomHelper.markup / overwrite / etc..
23925      * ?? should it be called with option to hide all editing features?
23926      */
23927     toObject : function()
23928     {
23929         
23930         var ret = {
23931             tag : 'table',
23932             contenteditable : 'false', // this stops cell selection from picking the table.
23933             'data-block' : 'Table',
23934             style : {
23935                 width:  this.width,
23936                 border : 'solid 1px #000', // ??? hard coded?
23937                 'border-collapse' : 'collapse' 
23938             },
23939             cn : [
23940                 { tag : 'tbody' , cn : [] }
23941             ]
23942         };
23943         
23944         // do we have a head = not really 
23945         var ncols = 0;
23946         Roo.each(this.rows, function( row ) {
23947             var tr = {
23948                 tag: 'tr',
23949                 style : {
23950                     margin: '6px',
23951                     border : 'solid 1px #000',
23952                     textAlign : 'left' 
23953                 },
23954                 cn : [ ]
23955             };
23956             
23957             ret.cn[0].cn.push(tr);
23958             // does the row have any properties? ?? height?
23959             var nc = 0;
23960             Roo.each(row, function( cell ) {
23961                 
23962                 var td = {
23963                     tag : 'td',
23964                     contenteditable :  'true',
23965                     'data-block' : 'Td',
23966                     html : cell.html,
23967                     style : cell.style
23968                 };
23969                 if (cell.colspan > 1) {
23970                     td.colspan = cell.colspan ;
23971                     nc += cell.colspan;
23972                 } else {
23973                     nc++;
23974                 }
23975                 if (cell.rowspan > 1) {
23976                     td.rowspan = cell.rowspan ;
23977                 }
23978                 
23979                 
23980                 // widths ?
23981                 tr.cn.push(td);
23982                     
23983                 
23984             }, this);
23985             ncols = Math.max(nc, ncols);
23986             
23987             
23988         }, this);
23989         // add the header row..
23990         
23991         ncols++;
23992          
23993         
23994         return ret;
23995          
23996     },
23997     
23998     readElement : function(node)
23999     {
24000         node  = node ? node : this.node ;
24001         this.width = this.getVal(node, true, 'style', 'width') || '100%';
24002         
24003         this.rows = [];
24004         this.no_row = 0;
24005         var trs = Array.from(node.rows);
24006         trs.forEach(function(tr) {
24007             var row =  [];
24008             this.rows.push(row);
24009             
24010             this.no_row++;
24011             var no_column = 0;
24012             Array.from(tr.cells).forEach(function(td) {
24013                 
24014                 var add = {
24015                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24016                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24017                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24018                     html : td.innerHTML
24019                 };
24020                 no_column += add.colspan;
24021                      
24022                 
24023                 row.push(add);
24024                 
24025                 
24026             },this);
24027             this.no_col = Math.max(this.no_col, no_column);
24028             
24029             
24030         },this);
24031         
24032         
24033     },
24034     normalizeRows: function()
24035     {
24036         var ret= [];
24037         var rid = -1;
24038         this.rows.forEach(function(row) {
24039             rid++;
24040             ret[rid] = [];
24041             row = this.normalizeRow(row);
24042             var cid = 0;
24043             row.forEach(function(c) {
24044                 while (typeof(ret[rid][cid]) != 'undefined') {
24045                     cid++;
24046                 }
24047                 if (typeof(ret[rid]) == 'undefined') {
24048                     ret[rid] = [];
24049                 }
24050                 ret[rid][cid] = c;
24051                 c.row = rid;
24052                 c.col = cid;
24053                 if (c.rowspan < 2) {
24054                     return;
24055                 }
24056                 
24057                 for(var i = 1 ;i < c.rowspan; i++) {
24058                     if (typeof(ret[rid+i]) == 'undefined') {
24059                         ret[rid+i] = [];
24060                     }
24061                     ret[rid+i][cid] = c;
24062                 }
24063             });
24064         }, this);
24065         return ret;
24066     
24067     },
24068     
24069     normalizeRow: function(row)
24070     {
24071         var ret= [];
24072         row.forEach(function(c) {
24073             if (c.colspan < 2) {
24074                 ret.push(c);
24075                 return;
24076             }
24077             for(var i =0 ;i < c.colspan; i++) {
24078                 ret.push(c);
24079             }
24080         });
24081         return ret;
24082     
24083     },
24084     
24085     deleteColumn : function(sel)
24086     {
24087         if (!sel || sel.type != 'col') {
24088             return;
24089         }
24090         if (this.no_col < 2) {
24091             return;
24092         }
24093         
24094         this.rows.forEach(function(row) {
24095             var cols = this.normalizeRow(row);
24096             var col = cols[sel.col];
24097             if (col.colspan > 1) {
24098                 col.colspan --;
24099             } else {
24100                 row.remove(col);
24101             }
24102             
24103         }, this);
24104         this.no_col--;
24105         
24106     },
24107     removeColumn : function()
24108     {
24109         this.deleteColumn({
24110             type: 'col',
24111             col : this.no_col-1
24112         });
24113         this.updateElement();
24114     },
24115     
24116      
24117     addColumn : function()
24118     {
24119         
24120         this.rows.forEach(function(row) {
24121             row.push(this.emptyCell());
24122            
24123         }, this);
24124         this.updateElement();
24125     },
24126     
24127     deleteRow : function(sel)
24128     {
24129         if (!sel || sel.type != 'row') {
24130             return;
24131         }
24132         
24133         if (this.no_row < 2) {
24134             return;
24135         }
24136         
24137         var rows = this.normalizeRows();
24138         
24139         
24140         rows[sel.row].forEach(function(col) {
24141             if (col.rowspan > 1) {
24142                 col.rowspan--;
24143             } else {
24144                 col.remove = 1; // flage it as removed.
24145             }
24146             
24147         }, this);
24148         var newrows = [];
24149         this.rows.forEach(function(row) {
24150             newrow = [];
24151             row.forEach(function(c) {
24152                 if (typeof(c.remove) == 'undefined') {
24153                     newrow.push(c);
24154                 }
24155                 
24156             });
24157             if (newrow.length > 0) {
24158                 newrows.push(row);
24159             }
24160         });
24161         this.rows =  newrows;
24162         
24163         
24164         
24165         this.no_row--;
24166         this.updateElement();
24167         
24168     },
24169     removeRow : function()
24170     {
24171         this.deleteRow({
24172             type: 'row',
24173             row : this.no_row-1
24174         });
24175         
24176     },
24177     
24178      
24179     addRow : function()
24180     {
24181         
24182         var row = [];
24183         for (var i = 0; i < this.no_col; i++ ) {
24184             
24185             row.push(this.emptyCell());
24186            
24187         }
24188         this.rows.push(row);
24189         this.updateElement();
24190         
24191     },
24192      
24193     // the default cell object... at present...
24194     emptyCell : function() {
24195         return (new Roo.htmleditor.BlockTd({})).toObject();
24196         
24197      
24198     },
24199     
24200     removeNode : function()
24201     {
24202         return this.node;
24203     },
24204     
24205     
24206     
24207     resetWidths : function()
24208     {
24209         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24210             var nn = Roo.htmleditor.Block.factory(n);
24211             nn.width = '';
24212             nn.updateElement(n);
24213         });
24214     }
24215     
24216     
24217     
24218     
24219 })
24220
24221 /**
24222  *
24223  * editing a TD?
24224  *
24225  * since selections really work on the table cell, then editing really should work from there
24226  *
24227  * The original plan was to support merging etc... - but that may not be needed yet..
24228  *
24229  * So this simple version will support:
24230  *   add/remove cols
24231  *   adjust the width +/-
24232  *   reset the width...
24233  *   
24234  *
24235  */
24236
24237
24238  
24239
24240 /**
24241  * @class Roo.htmleditor.BlockTable
24242  * Block that manages a table
24243  * 
24244  * @constructor
24245  * Create a new Filter.
24246  * @param {Object} config Configuration options
24247  */
24248
24249 Roo.htmleditor.BlockTd = function(cfg)
24250 {
24251     if (cfg.node) {
24252         this.readElement(cfg.node);
24253         this.updateElement(cfg.node);
24254     }
24255     Roo.apply(this, cfg);
24256      
24257     
24258     
24259 }
24260 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24261  
24262     node : false,
24263     
24264     width: '',
24265     textAlign : 'left',
24266     valign : 'top',
24267     
24268     colspan : 1,
24269     rowspan : 1,
24270     
24271     
24272     // used by context menu
24273     friendly_name : 'Table Cell',
24274     deleteTitle : false, // use our customer delete
24275     
24276     // context menu is drawn once..
24277     
24278     contextMenu : function(toolbar)
24279     {
24280         
24281         var cell = function() {
24282             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24283         };
24284         
24285         var table = function() {
24286             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24287         };
24288         
24289         var lr = false;
24290         var saveSel = function()
24291         {
24292             lr = toolbar.editorcore.getSelection().getRangeAt(0);
24293         }
24294         var restoreSel = function()
24295         {
24296             if (lr) {
24297                 (function() {
24298                     toolbar.editorcore.focus();
24299                     var cr = toolbar.editorcore.getSelection();
24300                     cr.removeAllRanges();
24301                     cr.addRange(lr);
24302                     toolbar.editorcore.onEditorEvent();
24303                 }).defer(10, this);
24304                 
24305                 
24306             }
24307         }
24308         
24309         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24310         
24311         var syncValue = toolbar.editorcore.syncValue;
24312         
24313         var fields = {};
24314         
24315         return [
24316             {
24317                 xtype : 'Button',
24318                 text : 'Edit Table',
24319                 listeners : {
24320                     click : function() {
24321                         var t = toolbar.tb.selectedNode.closest('table');
24322                         toolbar.editorcore.selectNode(t);
24323                         toolbar.editorcore.onEditorEvent();                        
24324                     }
24325                 }
24326                 
24327             },
24328               
24329            
24330              
24331             {
24332                 xtype : 'TextItem',
24333                 text : "Column Width: ",
24334                  xns : rooui.Toolbar 
24335                
24336             },
24337             {
24338                 xtype : 'Button',
24339                 text: '-',
24340                 listeners : {
24341                     click : function (_self, e)
24342                     {
24343                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24344                         cell().shrinkColumn();
24345                         syncValue();
24346                          toolbar.editorcore.onEditorEvent();
24347                     }
24348                 },
24349                 xns : rooui.Toolbar
24350             },
24351             {
24352                 xtype : 'Button',
24353                 text: '+',
24354                 listeners : {
24355                     click : function (_self, e)
24356                     {
24357                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24358                         cell().growColumn();
24359                         syncValue();
24360                         toolbar.editorcore.onEditorEvent();
24361                     }
24362                 },
24363                 xns : rooui.Toolbar
24364             },
24365             
24366             {
24367                 xtype : 'TextItem',
24368                 text : "Vertical Align: ",
24369                 xns : rooui.Toolbar  //Boostrap?
24370             },
24371             {
24372                 xtype : 'ComboBox',
24373                 allowBlank : false,
24374                 displayField : 'val',
24375                 editable : true,
24376                 listWidth : 100,
24377                 triggerAction : 'all',
24378                 typeAhead : true,
24379                 valueField : 'val',
24380                 width : 100,
24381                 name : 'valign',
24382                 listeners : {
24383                     select : function (combo, r, index)
24384                     {
24385                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24386                         var b = cell();
24387                         b.valign = r.get('val');
24388                         b.updateElement();
24389                         syncValue();
24390                         toolbar.editorcore.onEditorEvent();
24391                     }
24392                 },
24393                 xns : rooui.form,
24394                 store : {
24395                     xtype : 'SimpleStore',
24396                     data : [
24397                         ['top'],
24398                         ['middle'],
24399                         ['bottom'] // there are afew more... 
24400                     ],
24401                     fields : [ 'val'],
24402                     xns : Roo.data
24403                 }
24404             },
24405             
24406             {
24407                 xtype : 'TextItem',
24408                 text : "Merge Cells: ",
24409                  xns : rooui.Toolbar 
24410                
24411             },
24412             
24413             
24414             {
24415                 xtype : 'Button',
24416                 text: 'Right',
24417                 listeners : {
24418                     click : function (_self, e)
24419                     {
24420                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24421                         cell().mergeRight();
24422                         //block().growColumn();
24423                         syncValue();
24424                         toolbar.editorcore.onEditorEvent();
24425                     }
24426                 },
24427                 xns : rooui.Toolbar
24428             },
24429              
24430             {
24431                 xtype : 'Button',
24432                 text: 'Below',
24433                 listeners : {
24434                     click : function (_self, e)
24435                     {
24436                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24437                         cell().mergeBelow();
24438                         //block().growColumn();
24439                         syncValue();
24440                         toolbar.editorcore.onEditorEvent();
24441                     }
24442                 },
24443                 xns : rooui.Toolbar
24444             },
24445             {
24446                 xtype : 'TextItem',
24447                 text : "| ",
24448                  xns : rooui.Toolbar 
24449                
24450             },
24451             
24452             {
24453                 xtype : 'Button',
24454                 text: 'Split',
24455                 listeners : {
24456                     click : function (_self, e)
24457                     {
24458                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24459                         cell().split();
24460                         syncValue();
24461                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24462                         toolbar.editorcore.onEditorEvent();
24463                                              
24464                     }
24465                 },
24466                 xns : rooui.Toolbar
24467             },
24468             {
24469                 xtype : 'Fill',
24470                 xns : rooui.Toolbar 
24471                
24472             },
24473         
24474           
24475             {
24476                 xtype : 'Button',
24477                 text: 'Delete',
24478                  
24479                 xns : rooui.Toolbar,
24480                 menu : {
24481                     xtype : 'Menu',
24482                     xns : rooui.menu,
24483                     items : [
24484                         {
24485                             xtype : 'Item',
24486                             html: 'Column',
24487                             listeners : {
24488                                 click : function (_self, e)
24489                                 {
24490                                     var t = table();
24491                                     
24492                                     cell().deleteColumn();
24493                                     syncValue();
24494                                     toolbar.editorcore.selectNode(t.node);
24495                                     toolbar.editorcore.onEditorEvent();   
24496                                 }
24497                             },
24498                             xns : rooui.menu
24499                         },
24500                         {
24501                             xtype : 'Item',
24502                             html: 'Row',
24503                             listeners : {
24504                                 click : function (_self, e)
24505                                 {
24506                                     var t = table();
24507                                     cell().deleteRow();
24508                                     syncValue();
24509                                     
24510                                     toolbar.editorcore.selectNode(t.node);
24511                                     toolbar.editorcore.onEditorEvent();   
24512                                                          
24513                                 }
24514                             },
24515                             xns : rooui.menu
24516                         },
24517                        {
24518                             xtype : 'Separator',
24519                             xns : rooui.menu
24520                         },
24521                         {
24522                             xtype : 'Item',
24523                             html: 'Table',
24524                             listeners : {
24525                                 click : function (_self, e)
24526                                 {
24527                                     var t = table();
24528                                     var nn = t.node.nextSibling || t.node.previousSibling;
24529                                     t.node.parentNode.removeChild(t.node);
24530                                     if (nn) { 
24531                                         toolbar.editorcore.selectNode(nn, true);
24532                                     }
24533                                     toolbar.editorcore.onEditorEvent();   
24534                                                          
24535                                 }
24536                             },
24537                             xns : rooui.menu
24538                         }
24539                     ]
24540                 }
24541             }
24542             
24543             // align... << fixme
24544             
24545         ];
24546         
24547     },
24548     
24549     
24550   /**
24551      * create a DomHelper friendly object - for use with
24552      * Roo.DomHelper.markup / overwrite / etc..
24553      * ?? should it be called with option to hide all editing features?
24554      */
24555  /**
24556      * create a DomHelper friendly object - for use with
24557      * Roo.DomHelper.markup / overwrite / etc..
24558      * ?? should it be called with option to hide all editing features?
24559      */
24560     toObject : function()
24561     {
24562         
24563         var ret = {
24564             tag : 'td',
24565             contenteditable : 'true', // this stops cell selection from picking the table.
24566             'data-block' : 'Td',
24567             valign : this.valign,
24568             style : {  
24569                 'text-align' :  this.textAlign,
24570                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
24571                 'border-collapse' : 'collapse',
24572                 padding : '6px', // 8 for desktop / 4 for mobile
24573                 'vertical-align': this.valign
24574             },
24575             html : this.html
24576         };
24577         if (this.width != '') {
24578             ret.width = this.width;
24579             ret.style.width = this.width;
24580         }
24581         
24582         
24583         if (this.colspan > 1) {
24584             ret.colspan = this.colspan ;
24585         } 
24586         if (this.rowspan > 1) {
24587             ret.rowspan = this.rowspan ;
24588         }
24589         
24590            
24591         
24592         return ret;
24593          
24594     },
24595     
24596     readElement : function(node)
24597     {
24598         node  = node ? node : this.node ;
24599         this.width = node.style.width;
24600         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
24601         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
24602         this.html = node.innerHTML;
24603         
24604         
24605     },
24606      
24607     // the default cell object... at present...
24608     emptyCell : function() {
24609         return {
24610             colspan :  1,
24611             rowspan :  1,
24612             textAlign : 'left',
24613             html : "&nbsp;" // is this going to be editable now?
24614         };
24615      
24616     },
24617     
24618     removeNode : function()
24619     {
24620         return this.node.closest('table');
24621          
24622     },
24623     
24624     cellData : false,
24625     
24626     colWidths : false,
24627     
24628     toTableArray  : function()
24629     {
24630         var ret = [];
24631         var tab = this.node.closest('tr').closest('table');
24632         Array.from(tab.rows).forEach(function(r, ri){
24633             ret[ri] = [];
24634         });
24635         var rn = 0;
24636         this.colWidths = [];
24637         var all_auto = true;
24638         Array.from(tab.rows).forEach(function(r, ri){
24639             
24640             var cn = 0;
24641             Array.from(r.cells).forEach(function(ce, ci){
24642                 var c =  {
24643                     cell : ce,
24644                     row : rn,
24645                     col: cn,
24646                     colspan : ce.colSpan,
24647                     rowspan : ce.rowSpan
24648                 };
24649                 if (ce.isEqualNode(this.node)) {
24650                     this.cellData = c;
24651                 }
24652                 // if we have been filled up by a row?
24653                 if (typeof(ret[rn][cn]) != 'undefined') {
24654                     while(typeof(ret[rn][cn]) != 'undefined') {
24655                         cn++;
24656                     }
24657                     c.col = cn;
24658                 }
24659                 
24660                 if (typeof(this.colWidths[cn]) == 'undefined') {
24661                     this.colWidths[cn] =   ce.style.width;
24662                     if (this.colWidths[cn] != '') {
24663                         all_auto = false;
24664                     }
24665                 }
24666                 
24667                 
24668                 if (c.colspan < 2 && c.rowspan < 2 ) {
24669                     ret[rn][cn] = c;
24670                     cn++;
24671                     return;
24672                 }
24673                 for(var j = 0; j < c.rowspan; j++) {
24674                     if (typeof(ret[rn+j]) == 'undefined') {
24675                         continue; // we have a problem..
24676                     }
24677                     ret[rn+j][cn] = c;
24678                     for(var i = 0; i < c.colspan; i++) {
24679                         ret[rn+j][cn+i] = c;
24680                     }
24681                 }
24682                 
24683                 cn += c.colspan;
24684             }, this);
24685             rn++;
24686         }, this);
24687         
24688         // initalize widths.?
24689         // either all widths or no widths..
24690         if (all_auto) {
24691             this.colWidths[0] = false; // no widths flag.
24692         }
24693         
24694         
24695         return ret;
24696         
24697     },
24698     
24699     
24700     
24701     
24702     mergeRight: function()
24703     {
24704          
24705         // get the contents of the next cell along..
24706         var tr = this.node.closest('tr');
24707         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
24708         if (i >= tr.childNodes.length - 1) {
24709             return; // no cells on right to merge with.
24710         }
24711         var table = this.toTableArray();
24712         
24713         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
24714             return; // nothing right?
24715         }
24716         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
24717         // right cell - must be same rowspan and on the same row.
24718         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
24719             return; // right hand side is not same rowspan.
24720         }
24721         
24722         
24723         
24724         this.node.innerHTML += ' ' + rc.cell.innerHTML;
24725         tr.removeChild(rc.cell);
24726         this.colspan += rc.colspan;
24727         this.node.setAttribute('colspan', this.colspan);
24728
24729     },
24730     
24731     
24732     mergeBelow : function()
24733     {
24734         var table = this.toTableArray();
24735         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
24736             return; // no row below
24737         }
24738         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
24739             return; // nothing right?
24740         }
24741         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
24742         
24743         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
24744             return; // right hand side is not same rowspan.
24745         }
24746         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
24747         rc.cell.parentNode.removeChild(rc.cell);
24748         this.rowspan += rc.rowspan;
24749         this.node.setAttribute('rowspan', this.rowspan);
24750     },
24751     
24752     split: function()
24753     {
24754         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
24755             return;
24756         }
24757         var table = this.toTableArray();
24758         var cd = this.cellData;
24759         this.rowspan = 1;
24760         this.colspan = 1;
24761         
24762         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
24763             
24764             
24765             
24766             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
24767                 if (r == cd.row && c == cd.col) {
24768                     this.node.removeAttribute('rowspan');
24769                     this.node.removeAttribute('colspan');
24770                     continue;
24771                 }
24772                  
24773                 var ntd = this.node.cloneNode(); // which col/row should be 0..
24774                 ntd.removeAttribute('id'); //
24775                 //ntd.style.width  = '';
24776                 ntd.innerHTML = '';
24777                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
24778             }
24779             
24780         }
24781         this.redrawAllCells(table);
24782         
24783          
24784         
24785     },
24786     
24787     
24788     
24789     redrawAllCells: function(table)
24790     {
24791         
24792          
24793         var tab = this.node.closest('tr').closest('table');
24794         var ctr = tab.rows[0].parentNode;
24795         Array.from(tab.rows).forEach(function(r, ri){
24796             
24797             Array.from(r.cells).forEach(function(ce, ci){
24798                 ce.parentNode.removeChild(ce);
24799             });
24800             r.parentNode.removeChild(r);
24801         });
24802         for(var r = 0 ; r < table.length; r++) {
24803             var re = tab.rows[r];
24804             
24805             var re = tab.ownerDocument.createElement('tr');
24806             ctr.appendChild(re);
24807             for(var c = 0 ; c < table[r].length; c++) {
24808                 if (table[r][c].cell === false) {
24809                     continue;
24810                 }
24811                 
24812                 re.appendChild(table[r][c].cell);
24813                  
24814                 table[r][c].cell = false;
24815             }
24816         }
24817         
24818     },
24819     updateWidths : function(table)
24820     {
24821         for(var r = 0 ; r < table.length; r++) {
24822            
24823             for(var c = 0 ; c < table[r].length; c++) {
24824                 if (table[r][c].cell === false) {
24825                     continue;
24826                 }
24827                 
24828                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
24829                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
24830                     el.width = Math.floor(this.colWidths[c])  +'%';
24831                     el.updateElement(el.node);
24832                 }
24833                 table[r][c].cell = false; // done
24834             }
24835         }
24836     },
24837     normalizeWidths : function(table)
24838     {
24839     
24840         if (this.colWidths[0] === false) {
24841             var nw = 100.0 / this.colWidths.length;
24842             this.colWidths.forEach(function(w,i) {
24843                 this.colWidths[i] = nw;
24844             },this);
24845             return;
24846         }
24847     
24848         var t = 0, missing = [];
24849         
24850         this.colWidths.forEach(function(w,i) {
24851             //if you mix % and
24852             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
24853             var add =  this.colWidths[i];
24854             if (add > 0) {
24855                 t+=add;
24856                 return;
24857             }
24858             missing.push(i);
24859             
24860             
24861         },this);
24862         var nc = this.colWidths.length;
24863         if (missing.length) {
24864             var mult = (nc - missing.length) / (1.0 * nc);
24865             var t = mult * t;
24866             var ew = (100 -t) / (1.0 * missing.length);
24867             this.colWidths.forEach(function(w,i) {
24868                 if (w > 0) {
24869                     this.colWidths[i] = w * mult;
24870                     return;
24871                 }
24872                 
24873                 this.colWidths[i] = ew;
24874             }, this);
24875             // have to make up numbers..
24876              
24877         }
24878         // now we should have all the widths..
24879         
24880     
24881     },
24882     
24883     shrinkColumn : function()
24884     {
24885         var table = this.toTableArray();
24886         this.normalizeWidths(table);
24887         var col = this.cellData.col;
24888         var nw = this.colWidths[col] * 0.8;
24889         if (nw < 5) {
24890             return;
24891         }
24892         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24893         this.colWidths.forEach(function(w,i) {
24894             if (i == col) {
24895                  this.colWidths[i] = nw;
24896                 return;
24897             }
24898             this.colWidths[i] += otherAdd
24899         }, this);
24900         this.updateWidths(table);
24901          
24902     },
24903     growColumn : function()
24904     {
24905         var table = this.toTableArray();
24906         this.normalizeWidths(table);
24907         var col = this.cellData.col;
24908         var nw = this.colWidths[col] * 1.2;
24909         if (nw > 90) {
24910             return;
24911         }
24912         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24913         this.colWidths.forEach(function(w,i) {
24914             if (i == col) {
24915                 this.colWidths[i] = nw;
24916                 return;
24917             }
24918             this.colWidths[i] -= otherSub
24919         }, this);
24920         this.updateWidths(table);
24921          
24922     },
24923     deleteRow : function()
24924     {
24925         // delete this rows 'tr'
24926         // if any of the cells in this row have a rowspan > 1 && row!= this row..
24927         // then reduce the rowspan.
24928         var table = this.toTableArray();
24929         // this.cellData.row;
24930         for (var i =0;i< table[this.cellData.row].length ; i++) {
24931             var c = table[this.cellData.row][i];
24932             if (c.row != this.cellData.row) {
24933                 
24934                 c.rowspan--;
24935                 c.cell.setAttribute('rowspan', c.rowspan);
24936                 continue;
24937             }
24938             if (c.rowspan > 1) {
24939                 c.rowspan--;
24940                 c.cell.setAttribute('rowspan', c.rowspan);
24941             }
24942         }
24943         table.splice(this.cellData.row,1);
24944         this.redrawAllCells(table);
24945         
24946     },
24947     deleteColumn : function()
24948     {
24949         var table = this.toTableArray();
24950         
24951         for (var i =0;i< table.length ; i++) {
24952             var c = table[i][this.cellData.col];
24953             if (c.col != this.cellData.col) {
24954                 table[i][this.cellData.col].colspan--;
24955             } else if (c.colspan > 1) {
24956                 c.colspan--;
24957                 c.cell.setAttribute('colspan', c.colspan);
24958             }
24959             table[i].splice(this.cellData.col,1);
24960         }
24961         
24962         this.redrawAllCells(table);
24963     }
24964     
24965     
24966     
24967     
24968 })
24969
24970 //<script type="text/javascript">
24971
24972 /*
24973  * Based  Ext JS Library 1.1.1
24974  * Copyright(c) 2006-2007, Ext JS, LLC.
24975  * LGPL
24976  *
24977  */
24978  
24979 /**
24980  * @class Roo.HtmlEditorCore
24981  * @extends Roo.Component
24982  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24983  *
24984  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24985  */
24986
24987 Roo.HtmlEditorCore = function(config){
24988     
24989     
24990     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24991     
24992     
24993     this.addEvents({
24994         /**
24995          * @event initialize
24996          * Fires when the editor is fully initialized (including the iframe)
24997          * @param {Roo.HtmlEditorCore} this
24998          */
24999         initialize: true,
25000         /**
25001          * @event activate
25002          * Fires when the editor is first receives the focus. Any insertion must wait
25003          * until after this event.
25004          * @param {Roo.HtmlEditorCore} this
25005          */
25006         activate: true,
25007          /**
25008          * @event beforesync
25009          * Fires before the textarea is updated with content from the editor iframe. Return false
25010          * to cancel the sync.
25011          * @param {Roo.HtmlEditorCore} this
25012          * @param {String} html
25013          */
25014         beforesync: true,
25015          /**
25016          * @event beforepush
25017          * Fires before the iframe editor is updated with content from the textarea. Return false
25018          * to cancel the push.
25019          * @param {Roo.HtmlEditorCore} this
25020          * @param {String} html
25021          */
25022         beforepush: true,
25023          /**
25024          * @event sync
25025          * Fires when the textarea is updated with content from the editor iframe.
25026          * @param {Roo.HtmlEditorCore} this
25027          * @param {String} html
25028          */
25029         sync: true,
25030          /**
25031          * @event push
25032          * Fires when the iframe editor is updated with content from the textarea.
25033          * @param {Roo.HtmlEditorCore} this
25034          * @param {String} html
25035          */
25036         push: true,
25037         
25038         /**
25039          * @event editorevent
25040          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25041          * @param {Roo.HtmlEditorCore} this
25042          */
25043         editorevent: true 
25044          
25045         
25046     });
25047     
25048     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25049     
25050     // defaults : white / black...
25051     this.applyBlacklists();
25052     
25053     
25054     
25055 };
25056
25057
25058 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25059
25060
25061      /**
25062      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25063      */
25064     
25065     owner : false,
25066     
25067      /**
25068      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25069      *                        Roo.resizable.
25070      */
25071     resizable : false,
25072      /**
25073      * @cfg {Number} height (in pixels)
25074      */   
25075     height: 300,
25076    /**
25077      * @cfg {Number} width (in pixels)
25078      */   
25079     width: 500,
25080      /**
25081      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25082      *         if you are doing an email editor, this probably needs disabling, it's designed
25083      */
25084     autoClean: true,
25085     
25086     /**
25087      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25088      */
25089     enableBlocks : true,
25090     /**
25091      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25092      * 
25093      */
25094     stylesheets: false,
25095      /**
25096      * @cfg {String} language default en - language of text (usefull for rtl languages)
25097      * 
25098      */
25099     language: 'en',
25100     
25101     /**
25102      * @cfg {boolean} allowComments - default false - allow comments in HTML source
25103      *          - by default they are stripped - if you are editing email you may need this.
25104      */
25105     allowComments: false,
25106     // id of frame..
25107     frameId: false,
25108     
25109     // private properties
25110     validationEvent : false,
25111     deferHeight: true,
25112     initialized : false,
25113     activated : false,
25114     sourceEditMode : false,
25115     onFocus : Roo.emptyFn,
25116     iframePad:3,
25117     hideMode:'offsets',
25118     
25119     clearUp: true,
25120     
25121     // blacklist + whitelisted elements..
25122     black: false,
25123     white: false,
25124      
25125     bodyCls : '',
25126
25127     
25128     undoManager : false,
25129     /**
25130      * Protected method that will not generally be called directly. It
25131      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25132      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25133      */
25134     getDocMarkup : function(){
25135         // body styles..
25136         var st = '';
25137         
25138         // inherit styels from page...?? 
25139         if (this.stylesheets === false) {
25140             
25141             Roo.get(document.head).select('style').each(function(node) {
25142                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25143             });
25144             
25145             Roo.get(document.head).select('link').each(function(node) { 
25146                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25147             });
25148             
25149         } else if (!this.stylesheets.length) {
25150                 // simple..
25151                 st = '<style type="text/css">' +
25152                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25153                    '</style>';
25154         } else {
25155             for (var i in this.stylesheets) {
25156                 if (typeof(this.stylesheets[i]) != 'string') {
25157                     continue;
25158                 }
25159                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25160             }
25161             
25162         }
25163         
25164         st +=  '<style type="text/css">' +
25165             'IMG { cursor: pointer } ' +
25166         '</style>';
25167         
25168         st += '<meta name="google" content="notranslate">';
25169         
25170         var cls = 'notranslate roo-htmleditor-body';
25171         
25172         if(this.bodyCls.length){
25173             cls += ' ' + this.bodyCls;
25174         }
25175         
25176         return '<html  class="notranslate" translate="no"><head>' + st  +
25177             //<style type="text/css">' +
25178             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25179             //'</style>' +
25180             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25181     },
25182
25183     // private
25184     onRender : function(ct, position)
25185     {
25186         var _t = this;
25187         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25188         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25189         
25190         
25191         this.el.dom.style.border = '0 none';
25192         this.el.dom.setAttribute('tabIndex', -1);
25193         this.el.addClass('x-hidden hide');
25194         
25195         
25196         
25197         if(Roo.isIE){ // fix IE 1px bogus margin
25198             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25199         }
25200        
25201         
25202         this.frameId = Roo.id();
25203         
25204          
25205         
25206         var iframe = this.owner.wrap.createChild({
25207             tag: 'iframe',
25208             cls: 'form-control', // bootstrap..
25209             id: this.frameId,
25210             name: this.frameId,
25211             frameBorder : 'no',
25212             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25213         }, this.el
25214         );
25215         
25216         
25217         this.iframe = iframe.dom;
25218
25219         this.assignDocWin();
25220         
25221         this.doc.designMode = 'on';
25222        
25223         this.doc.open();
25224         this.doc.write(this.getDocMarkup());
25225         this.doc.close();
25226
25227         
25228         var task = { // must defer to wait for browser to be ready
25229             run : function(){
25230                 //console.log("run task?" + this.doc.readyState);
25231                 this.assignDocWin();
25232                 if(this.doc.body || this.doc.readyState == 'complete'){
25233                     try {
25234                         this.doc.designMode="on";
25235                         
25236                     } catch (e) {
25237                         return;
25238                     }
25239                     Roo.TaskMgr.stop(task);
25240                     this.initEditor.defer(10, this);
25241                 }
25242             },
25243             interval : 10,
25244             duration: 10000,
25245             scope: this
25246         };
25247         Roo.TaskMgr.start(task);
25248
25249     },
25250
25251     // private
25252     onResize : function(w, h)
25253     {
25254          Roo.log('resize: ' +w + ',' + h );
25255         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25256         if(!this.iframe){
25257             return;
25258         }
25259         if(typeof w == 'number'){
25260             
25261             this.iframe.style.width = w + 'px';
25262         }
25263         if(typeof h == 'number'){
25264             
25265             this.iframe.style.height = h + 'px';
25266             if(this.doc){
25267                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25268             }
25269         }
25270         
25271     },
25272
25273     /**
25274      * Toggles the editor between standard and source edit mode.
25275      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25276      */
25277     toggleSourceEdit : function(sourceEditMode){
25278         
25279         this.sourceEditMode = sourceEditMode === true;
25280         
25281         if(this.sourceEditMode){
25282  
25283             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
25284             
25285         }else{
25286             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25287             //this.iframe.className = '';
25288             this.deferFocus();
25289         }
25290         //this.setSize(this.owner.wrap.getSize());
25291         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25292     },
25293
25294     
25295   
25296
25297     /**
25298      * Protected method that will not generally be called directly. If you need/want
25299      * custom HTML cleanup, this is the method you should override.
25300      * @param {String} html The HTML to be cleaned
25301      * return {String} The cleaned HTML
25302      */
25303     cleanHtml : function(html)
25304     {
25305         html = String(html);
25306         if(html.length > 5){
25307             if(Roo.isSafari){ // strip safari nonsense
25308                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25309             }
25310         }
25311         if(html == '&nbsp;'){
25312             html = '';
25313         }
25314         return html;
25315     },
25316
25317     /**
25318      * HTML Editor -> Textarea
25319      * Protected method that will not generally be called directly. Syncs the contents
25320      * of the editor iframe with the textarea.
25321      */
25322     syncValue : function()
25323     {
25324         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25325         if(this.initialized){
25326             
25327             this.undoManager.addEvent();
25328
25329             
25330             var bd = (this.doc.body || this.doc.documentElement);
25331            
25332             
25333             var sel = this.win.getSelection();
25334             
25335             var div = document.createElement('div');
25336             div.innerHTML = bd.innerHTML;
25337             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25338             if (gtx.length > 0) {
25339                 var rm = gtx.item(0).parentNode;
25340                 rm.parentNode.removeChild(rm);
25341             }
25342             
25343            
25344             if (this.enableBlocks) {
25345                 new Roo.htmleditor.FilterBlock({ node : div });
25346             }
25347             //?? tidy?
25348             var tidy = new Roo.htmleditor.TidySerializer({
25349                 inner:  true
25350             });
25351             var html  = tidy.serialize(div);
25352             
25353             
25354             if(Roo.isSafari){
25355                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25356                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25357                 if(m && m[1]){
25358                     html = '<div style="'+m[0]+'">' + html + '</div>';
25359                 }
25360             }
25361             html = this.cleanHtml(html);
25362             // fix up the special chars.. normaly like back quotes in word...
25363             // however we do not want to do this with chinese..
25364             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25365                 
25366                 var cc = match.charCodeAt();
25367
25368                 // Get the character value, handling surrogate pairs
25369                 if (match.length == 2) {
25370                     // It's a surrogate pair, calculate the Unicode code point
25371                     var high = match.charCodeAt(0) - 0xD800;
25372                     var low  = match.charCodeAt(1) - 0xDC00;
25373                     cc = (high * 0x400) + low + 0x10000;
25374                 }  else if (
25375                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25376                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25377                     (cc >= 0xf900 && cc < 0xfb00 )
25378                 ) {
25379                         return match;
25380                 }  
25381          
25382                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25383                 return "&#" + cc + ";";
25384                 
25385                 
25386             });
25387             
25388             
25389              
25390             if(this.owner.fireEvent('beforesync', this, html) !== false){
25391                 this.el.dom.value = html;
25392                 this.owner.fireEvent('sync', this, html);
25393             }
25394         }
25395     },
25396
25397     /**
25398      * TEXTAREA -> EDITABLE
25399      * Protected method that will not generally be called directly. Pushes the value of the textarea
25400      * into the iframe editor.
25401      */
25402     pushValue : function()
25403     {
25404         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25405         if(this.initialized){
25406             var v = this.el.dom.value.trim();
25407             
25408             
25409             if(this.owner.fireEvent('beforepush', this, v) !== false){
25410                 var d = (this.doc.body || this.doc.documentElement);
25411                 d.innerHTML = v;
25412                  
25413                 this.el.dom.value = d.innerHTML;
25414                 this.owner.fireEvent('push', this, v);
25415             }
25416             if (this.autoClean) {
25417                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25418                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25419             }
25420             if (this.enableBlocks) {
25421                 Roo.htmleditor.Block.initAll(this.doc.body);
25422             }
25423             
25424             this.updateLanguage();
25425             
25426             var lc = this.doc.body.lastChild;
25427             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25428                 // add an extra line at the end.
25429                 this.doc.body.appendChild(this.doc.createElement('br'));
25430             }
25431             
25432             
25433         }
25434     },
25435
25436     // private
25437     deferFocus : function(){
25438         this.focus.defer(10, this);
25439     },
25440
25441     // doc'ed in Field
25442     focus : function(){
25443         if(this.win && !this.sourceEditMode){
25444             this.win.focus();
25445         }else{
25446             this.el.focus();
25447         }
25448     },
25449     
25450     assignDocWin: function()
25451     {
25452         var iframe = this.iframe;
25453         
25454          if(Roo.isIE){
25455             this.doc = iframe.contentWindow.document;
25456             this.win = iframe.contentWindow;
25457         } else {
25458 //            if (!Roo.get(this.frameId)) {
25459 //                return;
25460 //            }
25461 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25462 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25463             
25464             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25465                 return;
25466             }
25467             
25468             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25469             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25470         }
25471     },
25472     
25473     // private
25474     initEditor : function(){
25475         //console.log("INIT EDITOR");
25476         this.assignDocWin();
25477         
25478         
25479         
25480         this.doc.designMode="on";
25481         this.doc.open();
25482         this.doc.write(this.getDocMarkup());
25483         this.doc.close();
25484         
25485         var dbody = (this.doc.body || this.doc.documentElement);
25486         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25487         // this copies styles from the containing element into thsi one..
25488         // not sure why we need all of this..
25489         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25490         
25491         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25492         //ss['background-attachment'] = 'fixed'; // w3c
25493         dbody.bgProperties = 'fixed'; // ie
25494         dbody.setAttribute("translate", "no");
25495         
25496         //Roo.DomHelper.applyStyles(dbody, ss);
25497         Roo.EventManager.on(this.doc, {
25498              
25499             'mouseup': this.onEditorEvent,
25500             'dblclick': this.onEditorEvent,
25501             'click': this.onEditorEvent,
25502             'keyup': this.onEditorEvent,
25503             
25504             buffer:100,
25505             scope: this
25506         });
25507         Roo.EventManager.on(this.doc, {
25508             'paste': this.onPasteEvent,
25509             scope : this
25510         });
25511         if(Roo.isGecko){
25512             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25513         }
25514         //??? needed???
25515         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25516             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25517         }
25518         this.initialized = true;
25519
25520         
25521         // initialize special key events - enter
25522         new Roo.htmleditor.KeyEnter({core : this});
25523         
25524          
25525         
25526         this.owner.fireEvent('initialize', this);
25527         this.pushValue();
25528     },
25529     // this is to prevent a href clicks resulting in a redirect?
25530    
25531     onPasteEvent : function(e,v)
25532     {
25533         // I think we better assume paste is going to be a dirty load of rubish from word..
25534         
25535         // even pasting into a 'email version' of this widget will have to clean up that mess.
25536         var cd = (e.browserEvent.clipboardData || window.clipboardData);
25537         
25538         // check what type of paste - if it's an image, then handle it differently.
25539         if (cd.files.length > 0) {
25540             // pasting images?
25541             var urlAPI = (window.createObjectURL && window) || 
25542                 (window.URL && URL.revokeObjectURL && URL) || 
25543                 (window.webkitURL && webkitURL);
25544     
25545             var url = urlAPI.createObjectURL( cd.files[0]);
25546             this.insertAtCursor('<img src=" + url + ">');
25547             return false;
25548         }
25549         
25550         var html = cd.getData('text/html'); // clipboard event
25551         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25552         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25553         Roo.log(images);
25554         //Roo.log(imgs);
25555         // fixme..
25556         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
25557                        .map(function(g) { return g.toDataURL(); })
25558                        .filter(function(g) { return g != 'about:blank'; });
25559         
25560         
25561         html = this.cleanWordChars(html);
25562         
25563         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25564         
25565         
25566         var sn = this.getParentElement();
25567         // check if d contains a table, and prevent nesting??
25568         //Roo.log(d.getElementsByTagName('table'));
25569         //Roo.log(sn);
25570         //Roo.log(sn.closest('table'));
25571         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25572             e.preventDefault();
25573             this.insertAtCursor("You can not nest tables");
25574             //Roo.log("prevent?"); // fixme - 
25575             return false;
25576         }
25577         
25578         if (images.length > 0) {
25579             Roo.each(d.getElementsByTagName('img'), function(img, i) {
25580                 img.setAttribute('src', images[i]);
25581             });
25582         }
25583         if (this.autoClean) {
25584             new Roo.htmleditor.FilterStyleToTag({ node : d });
25585             new Roo.htmleditor.FilterAttributes({
25586                 node : d,
25587                 attrib_white : ['href', 'src', 'name', 'align'],
25588                 attrib_clean : ['href', 'src' ] 
25589             });
25590             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25591             // should be fonts..
25592             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
25593             new Roo.htmleditor.FilterParagraph({ node : d });
25594             new Roo.htmleditor.FilterSpan({ node : d });
25595             new Roo.htmleditor.FilterLongBr({ node : d });
25596         }
25597         if (this.enableBlocks) {
25598                 
25599             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25600                 if (img.closest('figure')) { // assume!! that it's aready
25601                     return;
25602                 }
25603                 var fig  = new Roo.htmleditor.BlockFigure({
25604                     image_src  : img.src
25605                 });
25606                 fig.updateElement(img); // replace it..
25607                 
25608             });
25609         }
25610         
25611         
25612         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
25613         if (this.enableBlocks) {
25614             Roo.htmleditor.Block.initAll(this.doc.body);
25615         }
25616         
25617         
25618         e.preventDefault();
25619         return false;
25620         // default behaveiour should be our local cleanup paste? (optional?)
25621         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25622         //this.owner.fireEvent('paste', e, v);
25623     },
25624     // private
25625     onDestroy : function(){
25626         
25627         
25628         
25629         if(this.rendered){
25630             
25631             //for (var i =0; i < this.toolbars.length;i++) {
25632             //    // fixme - ask toolbars for heights?
25633             //    this.toolbars[i].onDestroy();
25634            // }
25635             
25636             //this.wrap.dom.innerHTML = '';
25637             //this.wrap.remove();
25638         }
25639     },
25640
25641     // private
25642     onFirstFocus : function(){
25643         
25644         this.assignDocWin();
25645         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25646         
25647         this.activated = true;
25648          
25649     
25650         if(Roo.isGecko){ // prevent silly gecko errors
25651             this.win.focus();
25652             var s = this.win.getSelection();
25653             if(!s.focusNode || s.focusNode.nodeType != 3){
25654                 var r = s.getRangeAt(0);
25655                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25656                 r.collapse(true);
25657                 this.deferFocus();
25658             }
25659             try{
25660                 this.execCmd('useCSS', true);
25661                 this.execCmd('styleWithCSS', false);
25662             }catch(e){}
25663         }
25664         this.owner.fireEvent('activate', this);
25665     },
25666
25667     // private
25668     adjustFont: function(btn){
25669         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25670         //if(Roo.isSafari){ // safari
25671         //    adjust *= 2;
25672        // }
25673         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25674         if(Roo.isSafari){ // safari
25675             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25676             v =  (v < 10) ? 10 : v;
25677             v =  (v > 48) ? 48 : v;
25678             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25679             
25680         }
25681         
25682         
25683         v = Math.max(1, v+adjust);
25684         
25685         this.execCmd('FontSize', v  );
25686     },
25687
25688     onEditorEvent : function(e)
25689     {
25690          
25691         
25692         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25693             return; // we do not handle this.. (undo manager does..)
25694         }
25695         // in theory this detects if the last element is not a br, then we try and do that.
25696         // its so clicking in space at bottom triggers adding a br and moving the cursor.
25697         if (e &&
25698             e.target.nodeName == 'BODY' &&
25699             e.type == "mouseup" &&
25700             this.doc.body.lastChild
25701            ) {
25702             var lc = this.doc.body.lastChild;
25703             // gtx-trans is google translate plugin adding crap.
25704             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25705                 lc = lc.previousSibling;
25706             }
25707             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25708             // if last element is <BR> - then dont do anything.
25709             
25710                 var ns = this.doc.createElement('br');
25711                 this.doc.body.appendChild(ns);
25712                 range = this.doc.createRange();
25713                 range.setStartAfter(ns);
25714                 range.collapse(true);
25715                 var sel = this.win.getSelection();
25716                 sel.removeAllRanges();
25717                 sel.addRange(range);
25718             }
25719         }
25720         
25721         
25722         
25723         this.fireEditorEvent(e);
25724       //  this.updateToolbar();
25725         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25726     },
25727     
25728     fireEditorEvent: function(e)
25729     {
25730         this.owner.fireEvent('editorevent', this, e);
25731     },
25732
25733     insertTag : function(tg)
25734     {
25735         // could be a bit smarter... -> wrap the current selected tRoo..
25736         if (tg.toLowerCase() == 'span' ||
25737             tg.toLowerCase() == 'code' ||
25738             tg.toLowerCase() == 'sup' ||
25739             tg.toLowerCase() == 'sub' 
25740             ) {
25741             
25742             range = this.createRange(this.getSelection());
25743             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25744             wrappingNode.appendChild(range.extractContents());
25745             range.insertNode(wrappingNode);
25746
25747             return;
25748             
25749             
25750             
25751         }
25752         this.execCmd("formatblock",   tg);
25753         this.undoManager.addEvent(); 
25754     },
25755     
25756     insertText : function(txt)
25757     {
25758         
25759         
25760         var range = this.createRange();
25761         range.deleteContents();
25762                //alert(Sender.getAttribute('label'));
25763                
25764         range.insertNode(this.doc.createTextNode(txt));
25765         this.undoManager.addEvent();
25766     } ,
25767     
25768      
25769
25770     /**
25771      * Executes a Midas editor command on the editor document and performs necessary focus and
25772      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25773      * @param {String} cmd The Midas command
25774      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25775      */
25776     relayCmd : function(cmd, value)
25777     {
25778         
25779         switch (cmd) {
25780             case 'justifyleft':
25781             case 'justifyright':
25782             case 'justifycenter':
25783                 // if we are in a cell, then we will adjust the
25784                 var n = this.getParentElement();
25785                 var td = n.closest('td');
25786                 if (td) {
25787                     var bl = Roo.htmleditor.Block.factory(td);
25788                     bl.textAlign = cmd.replace('justify','');
25789                     bl.updateElement();
25790                     this.owner.fireEvent('editorevent', this);
25791                     return;
25792                 }
25793                 this.execCmd('styleWithCSS', true); // 
25794                 break;
25795             case 'bold':
25796             case 'italic':
25797                 // if there is no selection, then we insert, and set the curson inside it..
25798                 this.execCmd('styleWithCSS', false); 
25799                 break;
25800                 
25801         
25802             default:
25803                 break;
25804         }
25805         
25806         
25807         this.win.focus();
25808         this.execCmd(cmd, value);
25809         this.owner.fireEvent('editorevent', this);
25810         //this.updateToolbar();
25811         this.owner.deferFocus();
25812     },
25813
25814     /**
25815      * Executes a Midas editor command directly on the editor document.
25816      * For visual commands, you should use {@link #relayCmd} instead.
25817      * <b>This should only be called after the editor is initialized.</b>
25818      * @param {String} cmd The Midas command
25819      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25820      */
25821     execCmd : function(cmd, value){
25822         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25823         this.syncValue();
25824     },
25825  
25826  
25827    
25828     /**
25829      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25830      * to insert tRoo.
25831      * @param {String} text | dom node.. 
25832      */
25833     insertAtCursor : function(text)
25834     {
25835         
25836         if(!this.activated){
25837             return;
25838         }
25839          
25840         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25841             this.win.focus();
25842             
25843             
25844             // from jquery ui (MIT licenced)
25845             var range, node;
25846             var win = this.win;
25847             
25848             if (win.getSelection && win.getSelection().getRangeAt) {
25849                 
25850                 // delete the existing?
25851                 
25852                 this.createRange(this.getSelection()).deleteContents();
25853                 range = win.getSelection().getRangeAt(0);
25854                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25855                 range.insertNode(node);
25856                 range = range.cloneRange();
25857                 range.collapse(false);
25858                  
25859                 win.getSelection().removeAllRanges();
25860                 win.getSelection().addRange(range);
25861                 
25862                 
25863                 
25864             } else if (win.document.selection && win.document.selection.createRange) {
25865                 // no firefox support
25866                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25867                 win.document.selection.createRange().pasteHTML(txt);
25868             
25869             } else {
25870                 // no firefox support
25871                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25872                 this.execCmd('InsertHTML', txt);
25873             } 
25874             this.syncValue();
25875             
25876             this.deferFocus();
25877         }
25878     },
25879  // private
25880     mozKeyPress : function(e){
25881         if(e.ctrlKey){
25882             var c = e.getCharCode(), cmd;
25883           
25884             if(c > 0){
25885                 c = String.fromCharCode(c).toLowerCase();
25886                 switch(c){
25887                     case 'b':
25888                         cmd = 'bold';
25889                         break;
25890                     case 'i':
25891                         cmd = 'italic';
25892                         break;
25893                     
25894                     case 'u':
25895                         cmd = 'underline';
25896                         break;
25897                     
25898                     //case 'v':
25899                       //  this.cleanUpPaste.defer(100, this);
25900                       //  return;
25901                         
25902                 }
25903                 if(cmd){
25904                     
25905                     this.relayCmd(cmd);
25906                     //this.win.focus();
25907                     //this.execCmd(cmd);
25908                     //this.deferFocus();
25909                     e.preventDefault();
25910                 }
25911                 
25912             }
25913         }
25914     },
25915
25916     // private
25917     fixKeys : function(){ // load time branching for fastest keydown performance
25918         
25919         
25920         if(Roo.isIE){
25921             return function(e){
25922                 var k = e.getKey(), r;
25923                 if(k == e.TAB){
25924                     e.stopEvent();
25925                     r = this.doc.selection.createRange();
25926                     if(r){
25927                         r.collapse(true);
25928                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25929                         this.deferFocus();
25930                     }
25931                     return;
25932                 }
25933                 /// this is handled by Roo.htmleditor.KeyEnter
25934                  /*
25935                 if(k == e.ENTER){
25936                     r = this.doc.selection.createRange();
25937                     if(r){
25938                         var target = r.parentElement();
25939                         if(!target || target.tagName.toLowerCase() != 'li'){
25940                             e.stopEvent();
25941                             r.pasteHTML('<br/>');
25942                             r.collapse(false);
25943                             r.select();
25944                         }
25945                     }
25946                 }
25947                 */
25948                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25949                 //    this.cleanUpPaste.defer(100, this);
25950                 //    return;
25951                 //}
25952                 
25953                 
25954             };
25955         }else if(Roo.isOpera){
25956             return function(e){
25957                 var k = e.getKey();
25958                 if(k == e.TAB){
25959                     e.stopEvent();
25960                     this.win.focus();
25961                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25962                     this.deferFocus();
25963                 }
25964                
25965                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25966                 //    this.cleanUpPaste.defer(100, this);
25967                  //   return;
25968                 //}
25969                 
25970             };
25971         }else if(Roo.isSafari){
25972             return function(e){
25973                 var k = e.getKey();
25974                 
25975                 if(k == e.TAB){
25976                     e.stopEvent();
25977                     this.execCmd('InsertText','\t');
25978                     this.deferFocus();
25979                     return;
25980                 }
25981                  this.mozKeyPress(e);
25982                 
25983                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25984                  //   this.cleanUpPaste.defer(100, this);
25985                  //   return;
25986                // }
25987                 
25988              };
25989         }
25990     }(),
25991     
25992     getAllAncestors: function()
25993     {
25994         var p = this.getSelectedNode();
25995         var a = [];
25996         if (!p) {
25997             a.push(p); // push blank onto stack..
25998             p = this.getParentElement();
25999         }
26000         
26001         
26002         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26003             a.push(p);
26004             p = p.parentNode;
26005         }
26006         a.push(this.doc.body);
26007         return a;
26008     },
26009     lastSel : false,
26010     lastSelNode : false,
26011     
26012     
26013     getSelection : function() 
26014     {
26015         this.assignDocWin();
26016         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26017     },
26018     /**
26019      * Select a dom node
26020      * @param {DomElement} node the node to select
26021      */
26022     selectNode : function(node, collapse)
26023     {
26024         var nodeRange = node.ownerDocument.createRange();
26025         try {
26026             nodeRange.selectNode(node);
26027         } catch (e) {
26028             nodeRange.selectNodeContents(node);
26029         }
26030         if (collapse === true) {
26031             nodeRange.collapse(true);
26032         }
26033         //
26034         var s = this.win.getSelection();
26035         s.removeAllRanges();
26036         s.addRange(nodeRange);
26037     },
26038     
26039     getSelectedNode: function() 
26040     {
26041         // this may only work on Gecko!!!
26042         
26043         // should we cache this!!!!
26044         
26045          
26046          
26047         var range = this.createRange(this.getSelection()).cloneRange();
26048         
26049         if (Roo.isIE) {
26050             var parent = range.parentElement();
26051             while (true) {
26052                 var testRange = range.duplicate();
26053                 testRange.moveToElementText(parent);
26054                 if (testRange.inRange(range)) {
26055                     break;
26056                 }
26057                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26058                     break;
26059                 }
26060                 parent = parent.parentElement;
26061             }
26062             return parent;
26063         }
26064         
26065         // is ancestor a text element.
26066         var ac =  range.commonAncestorContainer;
26067         if (ac.nodeType == 3) {
26068             ac = ac.parentNode;
26069         }
26070         
26071         var ar = ac.childNodes;
26072          
26073         var nodes = [];
26074         var other_nodes = [];
26075         var has_other_nodes = false;
26076         for (var i=0;i<ar.length;i++) {
26077             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26078                 continue;
26079             }
26080             // fullly contained node.
26081             
26082             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26083                 nodes.push(ar[i]);
26084                 continue;
26085             }
26086             
26087             // probably selected..
26088             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26089                 other_nodes.push(ar[i]);
26090                 continue;
26091             }
26092             // outer..
26093             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26094                 continue;
26095             }
26096             
26097             
26098             has_other_nodes = true;
26099         }
26100         if (!nodes.length && other_nodes.length) {
26101             nodes= other_nodes;
26102         }
26103         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26104             return false;
26105         }
26106         
26107         return nodes[0];
26108     },
26109     
26110     
26111     createRange: function(sel)
26112     {
26113         // this has strange effects when using with 
26114         // top toolbar - not sure if it's a great idea.
26115         //this.editor.contentWindow.focus();
26116         if (typeof sel != "undefined") {
26117             try {
26118                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26119             } catch(e) {
26120                 return this.doc.createRange();
26121             }
26122         } else {
26123             return this.doc.createRange();
26124         }
26125     },
26126     getParentElement: function()
26127     {
26128         
26129         this.assignDocWin();
26130         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26131         
26132         var range = this.createRange(sel);
26133          
26134         try {
26135             var p = range.commonAncestorContainer;
26136             while (p.nodeType == 3) { // text node
26137                 p = p.parentNode;
26138             }
26139             return p;
26140         } catch (e) {
26141             return null;
26142         }
26143     
26144     },
26145     /***
26146      *
26147      * Range intersection.. the hard stuff...
26148      *  '-1' = before
26149      *  '0' = hits..
26150      *  '1' = after.
26151      *         [ -- selected range --- ]
26152      *   [fail]                        [fail]
26153      *
26154      *    basically..
26155      *      if end is before start or  hits it. fail.
26156      *      if start is after end or hits it fail.
26157      *
26158      *   if either hits (but other is outside. - then it's not 
26159      *   
26160      *    
26161      **/
26162     
26163     
26164     // @see http://www.thismuchiknow.co.uk/?p=64.
26165     rangeIntersectsNode : function(range, node)
26166     {
26167         var nodeRange = node.ownerDocument.createRange();
26168         try {
26169             nodeRange.selectNode(node);
26170         } catch (e) {
26171             nodeRange.selectNodeContents(node);
26172         }
26173     
26174         var rangeStartRange = range.cloneRange();
26175         rangeStartRange.collapse(true);
26176     
26177         var rangeEndRange = range.cloneRange();
26178         rangeEndRange.collapse(false);
26179     
26180         var nodeStartRange = nodeRange.cloneRange();
26181         nodeStartRange.collapse(true);
26182     
26183         var nodeEndRange = nodeRange.cloneRange();
26184         nodeEndRange.collapse(false);
26185     
26186         return rangeStartRange.compareBoundaryPoints(
26187                  Range.START_TO_START, nodeEndRange) == -1 &&
26188                rangeEndRange.compareBoundaryPoints(
26189                  Range.START_TO_START, nodeStartRange) == 1;
26190         
26191          
26192     },
26193     rangeCompareNode : function(range, node)
26194     {
26195         var nodeRange = node.ownerDocument.createRange();
26196         try {
26197             nodeRange.selectNode(node);
26198         } catch (e) {
26199             nodeRange.selectNodeContents(node);
26200         }
26201         
26202         
26203         range.collapse(true);
26204     
26205         nodeRange.collapse(true);
26206      
26207         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26208         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26209          
26210         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26211         
26212         var nodeIsBefore   =  ss == 1;
26213         var nodeIsAfter    = ee == -1;
26214         
26215         if (nodeIsBefore && nodeIsAfter) {
26216             return 0; // outer
26217         }
26218         if (!nodeIsBefore && nodeIsAfter) {
26219             return 1; //right trailed.
26220         }
26221         
26222         if (nodeIsBefore && !nodeIsAfter) {
26223             return 2;  // left trailed.
26224         }
26225         // fully contined.
26226         return 3;
26227     },
26228  
26229     cleanWordChars : function(input) {// change the chars to hex code
26230         
26231        var swapCodes  = [ 
26232             [    8211, "&#8211;" ], 
26233             [    8212, "&#8212;" ], 
26234             [    8216,  "'" ],  
26235             [    8217, "'" ],  
26236             [    8220, '"' ],  
26237             [    8221, '"' ],  
26238             [    8226, "*" ],  
26239             [    8230, "..." ]
26240         ]; 
26241         var output = input;
26242         Roo.each(swapCodes, function(sw) { 
26243             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26244             
26245             output = output.replace(swapper, sw[1]);
26246         });
26247         
26248         return output;
26249     },
26250     
26251      
26252     
26253         
26254     
26255     cleanUpChild : function (node)
26256     {
26257         
26258         new Roo.htmleditor.FilterComment({node : node});
26259         new Roo.htmleditor.FilterAttributes({
26260                 node : node,
26261                 attrib_black : this.ablack,
26262                 attrib_clean : this.aclean,
26263                 style_white : this.cwhite,
26264                 style_black : this.cblack
26265         });
26266         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26267         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26268          
26269         
26270     },
26271     
26272     /**
26273      * Clean up MS wordisms...
26274      * @deprecated - use filter directly
26275      */
26276     cleanWord : function(node)
26277     {
26278         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26279         
26280     },
26281    
26282     
26283     /**
26284
26285      * @deprecated - use filters
26286      */
26287     cleanTableWidths : function(node)
26288     {
26289         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26290         
26291  
26292     },
26293     
26294      
26295         
26296     applyBlacklists : function()
26297     {
26298         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26299         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26300         
26301         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
26302         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
26303         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
26304         
26305         this.white = [];
26306         this.black = [];
26307         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26308             if (b.indexOf(tag) > -1) {
26309                 return;
26310             }
26311             this.white.push(tag);
26312             
26313         }, this);
26314         
26315         Roo.each(w, function(tag) {
26316             if (b.indexOf(tag) > -1) {
26317                 return;
26318             }
26319             if (this.white.indexOf(tag) > -1) {
26320                 return;
26321             }
26322             this.white.push(tag);
26323             
26324         }, this);
26325         
26326         
26327         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26328             if (w.indexOf(tag) > -1) {
26329                 return;
26330             }
26331             this.black.push(tag);
26332             
26333         }, this);
26334         
26335         Roo.each(b, function(tag) {
26336             if (w.indexOf(tag) > -1) {
26337                 return;
26338             }
26339             if (this.black.indexOf(tag) > -1) {
26340                 return;
26341             }
26342             this.black.push(tag);
26343             
26344         }, this);
26345         
26346         
26347         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26348         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26349         
26350         this.cwhite = [];
26351         this.cblack = [];
26352         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26353             if (b.indexOf(tag) > -1) {
26354                 return;
26355             }
26356             this.cwhite.push(tag);
26357             
26358         }, this);
26359         
26360         Roo.each(w, function(tag) {
26361             if (b.indexOf(tag) > -1) {
26362                 return;
26363             }
26364             if (this.cwhite.indexOf(tag) > -1) {
26365                 return;
26366             }
26367             this.cwhite.push(tag);
26368             
26369         }, this);
26370         
26371         
26372         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26373             if (w.indexOf(tag) > -1) {
26374                 return;
26375             }
26376             this.cblack.push(tag);
26377             
26378         }, this);
26379         
26380         Roo.each(b, function(tag) {
26381             if (w.indexOf(tag) > -1) {
26382                 return;
26383             }
26384             if (this.cblack.indexOf(tag) > -1) {
26385                 return;
26386             }
26387             this.cblack.push(tag);
26388             
26389         }, this);
26390     },
26391     
26392     setStylesheets : function(stylesheets)
26393     {
26394         if(typeof(stylesheets) == 'string'){
26395             Roo.get(this.iframe.contentDocument.head).createChild({
26396                 tag : 'link',
26397                 rel : 'stylesheet',
26398                 type : 'text/css',
26399                 href : stylesheets
26400             });
26401             
26402             return;
26403         }
26404         var _this = this;
26405      
26406         Roo.each(stylesheets, function(s) {
26407             if(!s.length){
26408                 return;
26409             }
26410             
26411             Roo.get(_this.iframe.contentDocument.head).createChild({
26412                 tag : 'link',
26413                 rel : 'stylesheet',
26414                 type : 'text/css',
26415                 href : s
26416             });
26417         });
26418
26419         
26420     },
26421     
26422     
26423     updateLanguage : function()
26424     {
26425         if (!this.iframe || !this.iframe.contentDocument) {
26426             return;
26427         }
26428         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26429     },
26430     
26431     
26432     removeStylesheets : function()
26433     {
26434         var _this = this;
26435         
26436         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26437             s.remove();
26438         });
26439     },
26440     
26441     setStyle : function(style)
26442     {
26443         Roo.get(this.iframe.contentDocument.head).createChild({
26444             tag : 'style',
26445             type : 'text/css',
26446             html : style
26447         });
26448
26449         return;
26450     }
26451     
26452     // hide stuff that is not compatible
26453     /**
26454      * @event blur
26455      * @hide
26456      */
26457     /**
26458      * @event change
26459      * @hide
26460      */
26461     /**
26462      * @event focus
26463      * @hide
26464      */
26465     /**
26466      * @event specialkey
26467      * @hide
26468      */
26469     /**
26470      * @cfg {String} fieldClass @hide
26471      */
26472     /**
26473      * @cfg {String} focusClass @hide
26474      */
26475     /**
26476      * @cfg {String} autoCreate @hide
26477      */
26478     /**
26479      * @cfg {String} inputType @hide
26480      */
26481     /**
26482      * @cfg {String} invalidClass @hide
26483      */
26484     /**
26485      * @cfg {String} invalidText @hide
26486      */
26487     /**
26488      * @cfg {String} msgFx @hide
26489      */
26490     /**
26491      * @cfg {String} validateOnBlur @hide
26492      */
26493 });
26494
26495 Roo.HtmlEditorCore.white = [
26496         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26497         
26498        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
26499        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
26500        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
26501        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
26502        'TABLE',   'UL',         'XMP', 
26503        
26504        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
26505       'THEAD',   'TR', 
26506      
26507       'DIR', 'MENU', 'OL', 'UL', 'DL',
26508        
26509       'EMBED',  'OBJECT'
26510 ];
26511
26512
26513 Roo.HtmlEditorCore.black = [
26514     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26515         'APPLET', // 
26516         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
26517         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
26518         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
26519         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
26520         //'FONT' // CLEAN LATER..
26521         'COLGROUP', 'COL'   // messy tables.
26522         
26523         
26524 ];
26525 Roo.HtmlEditorCore.clean = [ // ?? needed???
26526      'SCRIPT', 'STYLE', 'TITLE', 'XML'
26527 ];
26528 Roo.HtmlEditorCore.tag_remove = [
26529     'FONT', 'TBODY'  
26530 ];
26531 // attributes..
26532
26533 Roo.HtmlEditorCore.ablack = [
26534     'on'
26535 ];
26536     
26537 Roo.HtmlEditorCore.aclean = [ 
26538     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26539 ];
26540
26541 // protocols..
26542 Roo.HtmlEditorCore.pwhite= [
26543         'http',  'https',  'mailto'
26544 ];
26545
26546 // white listed style attributes.
26547 Roo.HtmlEditorCore.cwhite= [
26548       //  'text-align', /// default is to allow most things..
26549       
26550          
26551 //        'font-size'//??
26552 ];
26553
26554 // black listed style attributes.
26555 Roo.HtmlEditorCore.cblack= [
26556       //  'font-size' -- this can be set by the project 
26557 ];
26558
26559
26560
26561
26562     //<script type="text/javascript">
26563
26564 /*
26565  * Ext JS Library 1.1.1
26566  * Copyright(c) 2006-2007, Ext JS, LLC.
26567  * Licence LGPL
26568  * 
26569  */
26570  
26571  
26572 Roo.form.HtmlEditor = function(config){
26573     
26574     
26575     
26576     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26577     
26578     if (!this.toolbars) {
26579         this.toolbars = [];
26580     }
26581     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26582     
26583     
26584 };
26585
26586 /**
26587  * @class Roo.form.HtmlEditor
26588  * @extends Roo.form.Field
26589  * Provides a lightweight HTML Editor component.
26590  *
26591  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26592  * 
26593  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26594  * supported by this editor.</b><br/><br/>
26595  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26596  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26597  */
26598 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26599     /**
26600      * @cfg {Boolean} clearUp
26601      */
26602     clearUp : true,
26603       /**
26604      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26605      */
26606     toolbars : false,
26607    
26608      /**
26609      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26610      *                        Roo.resizable.
26611      */
26612     resizable : false,
26613      /**
26614      * @cfg {Number} height (in pixels)
26615      */   
26616     height: 300,
26617    /**
26618      * @cfg {Number} width (in pixels)
26619      */   
26620     width: 500,
26621     
26622     /**
26623      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
26624      * 
26625      */
26626     stylesheets: false,
26627     
26628     
26629      /**
26630      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26631      * 
26632      */
26633     cblack: false,
26634     /**
26635      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26636      * 
26637      */
26638     cwhite: false,
26639     
26640      /**
26641      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26642      * 
26643      */
26644     black: false,
26645     /**
26646      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26647      * 
26648      */
26649     white: false,
26650     /**
26651      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26652      */
26653     allowComments: false,
26654     /**
26655      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26656      */
26657     enableBlocks : true,
26658     
26659     /**
26660      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26661      *         if you are doing an email editor, this probably needs disabling, it's designed
26662      */
26663     autoClean: true,
26664     /**
26665      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
26666      */
26667     bodyCls : '',
26668     /**
26669      * @cfg {String} language default en - language of text (usefull for rtl languages)
26670      * 
26671      */
26672     language: 'en',
26673     
26674      
26675     // id of frame..
26676     frameId: false,
26677     
26678     // private properties
26679     validationEvent : false,
26680     deferHeight: true,
26681     initialized : false,
26682     activated : false,
26683     
26684     onFocus : Roo.emptyFn,
26685     iframePad:3,
26686     hideMode:'offsets',
26687     
26688     actionMode : 'container', // defaults to hiding it...
26689     
26690     defaultAutoCreate : { // modified by initCompnoent..
26691         tag: "textarea",
26692         style:"width:500px;height:300px;",
26693         autocomplete: "new-password"
26694     },
26695
26696     // private
26697     initComponent : function(){
26698         this.addEvents({
26699             /**
26700              * @event initialize
26701              * Fires when the editor is fully initialized (including the iframe)
26702              * @param {HtmlEditor} this
26703              */
26704             initialize: true,
26705             /**
26706              * @event activate
26707              * Fires when the editor is first receives the focus. Any insertion must wait
26708              * until after this event.
26709              * @param {HtmlEditor} this
26710              */
26711             activate: true,
26712              /**
26713              * @event beforesync
26714              * Fires before the textarea is updated with content from the editor iframe. Return false
26715              * to cancel the sync.
26716              * @param {HtmlEditor} this
26717              * @param {String} html
26718              */
26719             beforesync: true,
26720              /**
26721              * @event beforepush
26722              * Fires before the iframe editor is updated with content from the textarea. Return false
26723              * to cancel the push.
26724              * @param {HtmlEditor} this
26725              * @param {String} html
26726              */
26727             beforepush: true,
26728              /**
26729              * @event sync
26730              * Fires when the textarea is updated with content from the editor iframe.
26731              * @param {HtmlEditor} this
26732              * @param {String} html
26733              */
26734             sync: true,
26735              /**
26736              * @event push
26737              * Fires when the iframe editor is updated with content from the textarea.
26738              * @param {HtmlEditor} this
26739              * @param {String} html
26740              */
26741             push: true,
26742              /**
26743              * @event editmodechange
26744              * Fires when the editor switches edit modes
26745              * @param {HtmlEditor} this
26746              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26747              */
26748             editmodechange: true,
26749             /**
26750              * @event editorevent
26751              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26752              * @param {HtmlEditor} this
26753              */
26754             editorevent: true,
26755             /**
26756              * @event firstfocus
26757              * Fires when on first focus - needed by toolbars..
26758              * @param {HtmlEditor} this
26759              */
26760             firstfocus: true,
26761             /**
26762              * @event autosave
26763              * Auto save the htmlEditor value as a file into Events
26764              * @param {HtmlEditor} this
26765              */
26766             autosave: true,
26767             /**
26768              * @event savedpreview
26769              * preview the saved version of htmlEditor
26770              * @param {HtmlEditor} this
26771              */
26772             savedpreview: true,
26773             
26774             /**
26775             * @event stylesheetsclick
26776             * Fires when press the Sytlesheets button
26777             * @param {Roo.HtmlEditorCore} this
26778             */
26779             stylesheetsclick: true,
26780             /**
26781             * @event paste
26782             * Fires when press user pastes into the editor
26783             * @param {Roo.HtmlEditorCore} this
26784             */
26785             paste: true 
26786         });
26787         this.defaultAutoCreate =  {
26788             tag: "textarea",
26789             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26790             autocomplete: "new-password"
26791         };
26792     },
26793
26794     /**
26795      * Protected method that will not generally be called directly. It
26796      * is called when the editor creates its toolbar. Override this method if you need to
26797      * add custom toolbar buttons.
26798      * @param {HtmlEditor} editor
26799      */
26800     createToolbar : function(editor){
26801         Roo.log("create toolbars");
26802         if (!editor.toolbars || !editor.toolbars.length) {
26803             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26804         }
26805         
26806         for (var i =0 ; i < editor.toolbars.length;i++) {
26807             editor.toolbars[i] = Roo.factory(
26808                     typeof(editor.toolbars[i]) == 'string' ?
26809                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26810                 Roo.form.HtmlEditor);
26811             editor.toolbars[i].init(editor);
26812         }
26813          
26814         
26815     },
26816     /**
26817      * get the Context selected node
26818      * @returns {DomElement|boolean} selected node if active or false if none
26819      * 
26820      */
26821     getSelectedNode : function()
26822     {
26823         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
26824             return false;
26825         }
26826         return this.toolbars[1].tb.selectedNode;
26827     
26828     },
26829     // private
26830     onRender : function(ct, position)
26831     {
26832         var _t = this;
26833         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26834         
26835         this.wrap = this.el.wrap({
26836             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26837         });
26838         
26839         this.editorcore.onRender(ct, position);
26840          
26841         if (this.resizable) {
26842             this.resizeEl = new Roo.Resizable(this.wrap, {
26843                 pinned : true,
26844                 wrap: true,
26845                 dynamic : true,
26846                 minHeight : this.height,
26847                 height: this.height,
26848                 handles : this.resizable,
26849                 width: this.width,
26850                 listeners : {
26851                     resize : function(r, w, h) {
26852                         _t.onResize(w,h); // -something
26853                     }
26854                 }
26855             });
26856             
26857         }
26858         this.createToolbar(this);
26859        
26860         
26861         if(!this.width){
26862             this.setSize(this.wrap.getSize());
26863         }
26864         if (this.resizeEl) {
26865             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26866             // should trigger onReize..
26867         }
26868         
26869         this.keyNav = new Roo.KeyNav(this.el, {
26870             
26871             "tab" : function(e){
26872                 e.preventDefault();
26873                 
26874                 var value = this.getValue();
26875                 
26876                 var start = this.el.dom.selectionStart;
26877                 var end = this.el.dom.selectionEnd;
26878                 
26879                 if(!e.shiftKey){
26880                     
26881                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26882                     this.el.dom.setSelectionRange(end + 1, end + 1);
26883                     return;
26884                 }
26885                 
26886                 var f = value.substring(0, start).split("\t");
26887                 
26888                 if(f.pop().length != 0){
26889                     return;
26890                 }
26891                 
26892                 this.setValue(f.join("\t") + value.substring(end));
26893                 this.el.dom.setSelectionRange(start - 1, start - 1);
26894                 
26895             },
26896             
26897             "home" : function(e){
26898                 e.preventDefault();
26899                 
26900                 var curr = this.el.dom.selectionStart;
26901                 var lines = this.getValue().split("\n");
26902                 
26903                 if(!lines.length){
26904                     return;
26905                 }
26906                 
26907                 if(e.ctrlKey){
26908                     this.el.dom.setSelectionRange(0, 0);
26909                     return;
26910                 }
26911                 
26912                 var pos = 0;
26913                 
26914                 for (var i = 0; i < lines.length;i++) {
26915                     pos += lines[i].length;
26916                     
26917                     if(i != 0){
26918                         pos += 1;
26919                     }
26920                     
26921                     if(pos < curr){
26922                         continue;
26923                     }
26924                     
26925                     pos -= lines[i].length;
26926                     
26927                     break;
26928                 }
26929                 
26930                 if(!e.shiftKey){
26931                     this.el.dom.setSelectionRange(pos, pos);
26932                     return;
26933                 }
26934                 
26935                 this.el.dom.selectionStart = pos;
26936                 this.el.dom.selectionEnd = curr;
26937             },
26938             
26939             "end" : function(e){
26940                 e.preventDefault();
26941                 
26942                 var curr = this.el.dom.selectionStart;
26943                 var lines = this.getValue().split("\n");
26944                 
26945                 if(!lines.length){
26946                     return;
26947                 }
26948                 
26949                 if(e.ctrlKey){
26950                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26951                     return;
26952                 }
26953                 
26954                 var pos = 0;
26955                 
26956                 for (var i = 0; i < lines.length;i++) {
26957                     
26958                     pos += lines[i].length;
26959                     
26960                     if(i != 0){
26961                         pos += 1;
26962                     }
26963                     
26964                     if(pos < curr){
26965                         continue;
26966                     }
26967                     
26968                     break;
26969                 }
26970                 
26971                 if(!e.shiftKey){
26972                     this.el.dom.setSelectionRange(pos, pos);
26973                     return;
26974                 }
26975                 
26976                 this.el.dom.selectionStart = curr;
26977                 this.el.dom.selectionEnd = pos;
26978             },
26979
26980             scope : this,
26981
26982             doRelay : function(foo, bar, hname){
26983                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26984             },
26985
26986             forceKeyDown: true
26987         });
26988         
26989 //        if(this.autosave && this.w){
26990 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26991 //        }
26992     },
26993
26994     // private
26995     onResize : function(w, h)
26996     {
26997         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26998         var ew = false;
26999         var eh = false;
27000         
27001         if(this.el ){
27002             if(typeof w == 'number'){
27003                 var aw = w - this.wrap.getFrameWidth('lr');
27004                 this.el.setWidth(this.adjustWidth('textarea', aw));
27005                 ew = aw;
27006             }
27007             if(typeof h == 'number'){
27008                 var tbh = 0;
27009                 for (var i =0; i < this.toolbars.length;i++) {
27010                     // fixme - ask toolbars for heights?
27011                     tbh += this.toolbars[i].tb.el.getHeight();
27012                     if (this.toolbars[i].footer) {
27013                         tbh += this.toolbars[i].footer.el.getHeight();
27014                     }
27015                 }
27016                 
27017                 
27018                 
27019                 
27020                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27021                 ah -= 5; // knock a few pixes off for look..
27022 //                Roo.log(ah);
27023                 this.el.setHeight(this.adjustWidth('textarea', ah));
27024                 var eh = ah;
27025             }
27026         }
27027         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27028         this.editorcore.onResize(ew,eh);
27029         
27030     },
27031
27032     /**
27033      * Toggles the editor between standard and source edit mode.
27034      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27035      */
27036     toggleSourceEdit : function(sourceEditMode)
27037     {
27038         this.editorcore.toggleSourceEdit(sourceEditMode);
27039         
27040         if(this.editorcore.sourceEditMode){
27041             Roo.log('editor - showing textarea');
27042             
27043 //            Roo.log('in');
27044 //            Roo.log(this.syncValue());
27045             this.editorcore.syncValue();
27046             this.el.removeClass('x-hidden');
27047             this.el.dom.removeAttribute('tabIndex');
27048             this.el.focus();
27049             this.el.dom.scrollTop = 0;
27050             
27051             
27052             for (var i = 0; i < this.toolbars.length; i++) {
27053                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27054                     this.toolbars[i].tb.hide();
27055                     this.toolbars[i].footer.hide();
27056                 }
27057             }
27058             
27059         }else{
27060             Roo.log('editor - hiding textarea');
27061 //            Roo.log('out')
27062 //            Roo.log(this.pushValue()); 
27063             this.editorcore.pushValue();
27064             
27065             this.el.addClass('x-hidden');
27066             this.el.dom.setAttribute('tabIndex', -1);
27067             
27068             for (var i = 0; i < this.toolbars.length; i++) {
27069                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27070                     this.toolbars[i].tb.show();
27071                     this.toolbars[i].footer.show();
27072                 }
27073             }
27074             
27075             //this.deferFocus();
27076         }
27077         
27078         this.setSize(this.wrap.getSize());
27079         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27080         
27081         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27082     },
27083  
27084     // private (for BoxComponent)
27085     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27086
27087     // private (for BoxComponent)
27088     getResizeEl : function(){
27089         return this.wrap;
27090     },
27091
27092     // private (for BoxComponent)
27093     getPositionEl : function(){
27094         return this.wrap;
27095     },
27096
27097     // private
27098     initEvents : function(){
27099         this.originalValue = this.getValue();
27100     },
27101
27102     /**
27103      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27104      * @method
27105      */
27106     markInvalid : Roo.emptyFn,
27107     /**
27108      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27109      * @method
27110      */
27111     clearInvalid : Roo.emptyFn,
27112
27113     setValue : function(v){
27114         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27115         this.editorcore.pushValue();
27116     },
27117
27118     /**
27119      * update the language in the body - really done by core
27120      * @param {String} language - eg. en / ar / zh-CN etc..
27121      */
27122     updateLanguage : function(lang)
27123     {
27124         this.language = lang;
27125         this.editorcore.language = lang;
27126         this.editorcore.updateLanguage();
27127      
27128     },
27129     // private
27130     deferFocus : function(){
27131         this.focus.defer(10, this);
27132     },
27133
27134     // doc'ed in Field
27135     focus : function(){
27136         this.editorcore.focus();
27137         
27138     },
27139       
27140
27141     // private
27142     onDestroy : function(){
27143         
27144         
27145         
27146         if(this.rendered){
27147             
27148             for (var i =0; i < this.toolbars.length;i++) {
27149                 // fixme - ask toolbars for heights?
27150                 this.toolbars[i].onDestroy();
27151             }
27152             
27153             this.wrap.dom.innerHTML = '';
27154             this.wrap.remove();
27155         }
27156     },
27157
27158     // private
27159     onFirstFocus : function(){
27160         //Roo.log("onFirstFocus");
27161         this.editorcore.onFirstFocus();
27162          for (var i =0; i < this.toolbars.length;i++) {
27163             this.toolbars[i].onFirstFocus();
27164         }
27165         
27166     },
27167     
27168     // private
27169     syncValue : function()
27170     {
27171         this.editorcore.syncValue();
27172     },
27173     
27174     pushValue : function()
27175     {
27176         this.editorcore.pushValue();
27177     },
27178     
27179     setStylesheets : function(stylesheets)
27180     {
27181         this.editorcore.setStylesheets(stylesheets);
27182     },
27183     
27184     removeStylesheets : function()
27185     {
27186         this.editorcore.removeStylesheets();
27187     }
27188      
27189     
27190     // hide stuff that is not compatible
27191     /**
27192      * @event blur
27193      * @hide
27194      */
27195     /**
27196      * @event change
27197      * @hide
27198      */
27199     /**
27200      * @event focus
27201      * @hide
27202      */
27203     /**
27204      * @event specialkey
27205      * @hide
27206      */
27207     /**
27208      * @cfg {String} fieldClass @hide
27209      */
27210     /**
27211      * @cfg {String} focusClass @hide
27212      */
27213     /**
27214      * @cfg {String} autoCreate @hide
27215      */
27216     /**
27217      * @cfg {String} inputType @hide
27218      */
27219     /**
27220      * @cfg {String} invalidClass @hide
27221      */
27222     /**
27223      * @cfg {String} invalidText @hide
27224      */
27225     /**
27226      * @cfg {String} msgFx @hide
27227      */
27228     /**
27229      * @cfg {String} validateOnBlur @hide
27230      */
27231 });
27232  
27233     /*
27234  * Based on
27235  * Ext JS Library 1.1.1
27236  * Copyright(c) 2006-2007, Ext JS, LLC.
27237  *  
27238  
27239  */
27240
27241 /**
27242  * @class Roo.form.HtmlEditor.ToolbarStandard
27243  * Basic Toolbar
27244
27245  * Usage:
27246  *
27247  new Roo.form.HtmlEditor({
27248     ....
27249     toolbars : [
27250         new Roo.form.HtmlEditorToolbar1({
27251             disable : { fonts: 1 , format: 1, ..., ... , ...],
27252             btns : [ .... ]
27253         })
27254     }
27255      
27256  * 
27257  * @cfg {Object} disable List of elements to disable..
27258  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27259  * 
27260  * 
27261  * NEEDS Extra CSS? 
27262  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27263  */
27264  
27265 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27266 {
27267     
27268     Roo.apply(this, config);
27269     
27270     // default disabled, based on 'good practice'..
27271     this.disable = this.disable || {};
27272     Roo.applyIf(this.disable, {
27273         fontSize : true,
27274         colors : true,
27275         specialElements : true
27276     });
27277     
27278     
27279     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27280     // dont call parent... till later.
27281 }
27282
27283 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27284     
27285     tb: false,
27286     
27287     rendered: false,
27288     
27289     editor : false,
27290     editorcore : false,
27291     /**
27292      * @cfg {Object} disable  List of toolbar elements to disable
27293          
27294      */
27295     disable : false,
27296     
27297     
27298      /**
27299      * @cfg {String} createLinkText The default text for the create link prompt
27300      */
27301     createLinkText : 'Please enter the URL for the link:',
27302     /**
27303      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27304      */
27305     defaultLinkValue : 'http:/'+'/',
27306    
27307     
27308       /**
27309      * @cfg {Array} fontFamilies An array of available font families
27310      */
27311     fontFamilies : [
27312         'Arial',
27313         'Courier New',
27314         'Tahoma',
27315         'Times New Roman',
27316         'Verdana'
27317     ],
27318     
27319     specialChars : [
27320            "&#169;",
27321           "&#174;",     
27322           "&#8482;",    
27323           "&#163;" ,    
27324          // "&#8212;",    
27325           "&#8230;",    
27326           "&#247;" ,    
27327         //  "&#225;" ,     ?? a acute?
27328            "&#8364;"    , //Euro
27329        //   "&#8220;"    ,
27330         //  "&#8221;"    ,
27331         //  "&#8226;"    ,
27332           "&#176;"  //   , // degrees
27333
27334          // "&#233;"     , // e ecute
27335          // "&#250;"     , // u ecute?
27336     ],
27337     
27338     specialElements : [
27339         {
27340             text: "Insert Table",
27341             xtype: 'MenuItem',
27342             xns : Roo.Menu,
27343             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27344                 
27345         },
27346         {    
27347             text: "Insert Image",
27348             xtype: 'MenuItem',
27349             xns : Roo.Menu,
27350             ihtml : '<img src="about:blank"/>'
27351             
27352         }
27353         
27354          
27355     ],
27356     
27357     
27358     inputElements : [ 
27359             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27360             "input:submit", "input:button", "select", "textarea", "label" ],
27361     formats : [
27362         ["p"] ,  
27363         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27364         ["pre"],[ "code"], 
27365         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27366         ['div'],['span'],
27367         ['sup'],['sub']
27368     ],
27369     
27370     cleanStyles : [
27371         "font-size"
27372     ],
27373      /**
27374      * @cfg {String} defaultFont default font to use.
27375      */
27376     defaultFont: 'tahoma',
27377    
27378     fontSelect : false,
27379     
27380     
27381     formatCombo : false,
27382     
27383     init : function(editor)
27384     {
27385         this.editor = editor;
27386         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27387         var editorcore = this.editorcore;
27388         
27389         var _t = this;
27390         
27391         var fid = editorcore.frameId;
27392         var etb = this;
27393         function btn(id, toggle, handler){
27394             var xid = fid + '-'+ id ;
27395             return {
27396                 id : xid,
27397                 cmd : id,
27398                 cls : 'x-btn-icon x-edit-'+id,
27399                 enableToggle:toggle !== false,
27400                 scope: _t, // was editor...
27401                 handler:handler||_t.relayBtnCmd,
27402                 clickEvent:'mousedown',
27403                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27404                 tabIndex:-1
27405             };
27406         }
27407         
27408         
27409         
27410         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27411         this.tb = tb;
27412          // stop form submits
27413         tb.el.on('click', function(e){
27414             e.preventDefault(); // what does this do?
27415         });
27416
27417         if(!this.disable.font) { // && !Roo.isSafari){
27418             /* why no safari for fonts 
27419             editor.fontSelect = tb.el.createChild({
27420                 tag:'select',
27421                 tabIndex: -1,
27422                 cls:'x-font-select',
27423                 html: this.createFontOptions()
27424             });
27425             
27426             editor.fontSelect.on('change', function(){
27427                 var font = editor.fontSelect.dom.value;
27428                 editor.relayCmd('fontname', font);
27429                 editor.deferFocus();
27430             }, editor);
27431             
27432             tb.add(
27433                 editor.fontSelect.dom,
27434                 '-'
27435             );
27436             */
27437             
27438         };
27439         if(!this.disable.formats){
27440             this.formatCombo = new Roo.form.ComboBox({
27441                 store: new Roo.data.SimpleStore({
27442                     id : 'tag',
27443                     fields: ['tag'],
27444                     data : this.formats // from states.js
27445                 }),
27446                 blockFocus : true,
27447                 name : '',
27448                 //autoCreate : {tag: "div",  size: "20"},
27449                 displayField:'tag',
27450                 typeAhead: false,
27451                 mode: 'local',
27452                 editable : false,
27453                 triggerAction: 'all',
27454                 emptyText:'Add tag',
27455                 selectOnFocus:true,
27456                 width:135,
27457                 listeners : {
27458                     'select': function(c, r, i) {
27459                         editorcore.insertTag(r.get('tag'));
27460                         editor.focus();
27461                     }
27462                 }
27463
27464             });
27465             tb.addField(this.formatCombo);
27466             
27467         }
27468         
27469         if(!this.disable.format){
27470             tb.add(
27471                 btn('bold'),
27472                 btn('italic'),
27473                 btn('underline'),
27474                 btn('strikethrough')
27475             );
27476         };
27477         if(!this.disable.fontSize){
27478             tb.add(
27479                 '-',
27480                 
27481                 
27482                 btn('increasefontsize', false, editorcore.adjustFont),
27483                 btn('decreasefontsize', false, editorcore.adjustFont)
27484             );
27485         };
27486         
27487         
27488         if(!this.disable.colors){
27489             tb.add(
27490                 '-', {
27491                     id:editorcore.frameId +'-forecolor',
27492                     cls:'x-btn-icon x-edit-forecolor',
27493                     clickEvent:'mousedown',
27494                     tooltip: this.buttonTips['forecolor'] || undefined,
27495                     tabIndex:-1,
27496                     menu : new Roo.menu.ColorMenu({
27497                         allowReselect: true,
27498                         focus: Roo.emptyFn,
27499                         value:'000000',
27500                         plain:true,
27501                         selectHandler: function(cp, color){
27502                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27503                             editor.deferFocus();
27504                         },
27505                         scope: editorcore,
27506                         clickEvent:'mousedown'
27507                     })
27508                 }, {
27509                     id:editorcore.frameId +'backcolor',
27510                     cls:'x-btn-icon x-edit-backcolor',
27511                     clickEvent:'mousedown',
27512                     tooltip: this.buttonTips['backcolor'] || undefined,
27513                     tabIndex:-1,
27514                     menu : new Roo.menu.ColorMenu({
27515                         focus: Roo.emptyFn,
27516                         value:'FFFFFF',
27517                         plain:true,
27518                         allowReselect: true,
27519                         selectHandler: function(cp, color){
27520                             if(Roo.isGecko){
27521                                 editorcore.execCmd('useCSS', false);
27522                                 editorcore.execCmd('hilitecolor', color);
27523                                 editorcore.execCmd('useCSS', true);
27524                                 editor.deferFocus();
27525                             }else{
27526                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27527                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27528                                 editor.deferFocus();
27529                             }
27530                         },
27531                         scope:editorcore,
27532                         clickEvent:'mousedown'
27533                     })
27534                 }
27535             );
27536         };
27537         // now add all the items...
27538         
27539
27540         if(!this.disable.alignments){
27541             tb.add(
27542                 '-',
27543                 btn('justifyleft'),
27544                 btn('justifycenter'),
27545                 btn('justifyright')
27546             );
27547         };
27548
27549         //if(!Roo.isSafari){
27550             if(!this.disable.links){
27551                 tb.add(
27552                     '-',
27553                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27554                 );
27555             };
27556
27557             if(!this.disable.lists){
27558                 tb.add(
27559                     '-',
27560                     btn('insertorderedlist'),
27561                     btn('insertunorderedlist')
27562                 );
27563             }
27564             if(!this.disable.sourceEdit){
27565                 tb.add(
27566                     '-',
27567                     btn('sourceedit', true, function(btn){
27568                         this.toggleSourceEdit(btn.pressed);
27569                     })
27570                 );
27571             }
27572         //}
27573         
27574         var smenu = { };
27575         // special menu.. - needs to be tidied up..
27576         if (!this.disable.special) {
27577             smenu = {
27578                 text: "&#169;",
27579                 cls: 'x-edit-none',
27580                 
27581                 menu : {
27582                     items : []
27583                 }
27584             };
27585             for (var i =0; i < this.specialChars.length; i++) {
27586                 smenu.menu.items.push({
27587                     
27588                     html: this.specialChars[i],
27589                     handler: function(a,b) {
27590                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27591                         //editor.insertAtCursor(a.html);
27592                         
27593                     },
27594                     tabIndex:-1
27595                 });
27596             }
27597             
27598             
27599             tb.add(smenu);
27600             
27601             
27602         }
27603         
27604         var cmenu = { };
27605         if (!this.disable.cleanStyles) {
27606             cmenu = {
27607                 cls: 'x-btn-icon x-btn-clear',
27608                 
27609                 menu : {
27610                     items : []
27611                 }
27612             };
27613             for (var i =0; i < this.cleanStyles.length; i++) {
27614                 cmenu.menu.items.push({
27615                     actiontype : this.cleanStyles[i],
27616                     html: 'Remove ' + this.cleanStyles[i],
27617                     handler: function(a,b) {
27618 //                        Roo.log(a);
27619 //                        Roo.log(b);
27620                         var c = Roo.get(editorcore.doc.body);
27621                         c.select('[style]').each(function(s) {
27622                             s.dom.style.removeProperty(a.actiontype);
27623                         });
27624                         editorcore.syncValue();
27625                     },
27626                     tabIndex:-1
27627                 });
27628             }
27629             cmenu.menu.items.push({
27630                 actiontype : 'tablewidths',
27631                 html: 'Remove Table Widths',
27632                 handler: function(a,b) {
27633                     editorcore.cleanTableWidths();
27634                     editorcore.syncValue();
27635                 },
27636                 tabIndex:-1
27637             });
27638             cmenu.menu.items.push({
27639                 actiontype : 'word',
27640                 html: 'Remove MS Word Formating',
27641                 handler: function(a,b) {
27642                     editorcore.cleanWord();
27643                     editorcore.syncValue();
27644                 },
27645                 tabIndex:-1
27646             });
27647             
27648             cmenu.menu.items.push({
27649                 actiontype : 'all',
27650                 html: 'Remove All Styles',
27651                 handler: function(a,b) {
27652                     
27653                     var c = Roo.get(editorcore.doc.body);
27654                     c.select('[style]').each(function(s) {
27655                         s.dom.removeAttribute('style');
27656                     });
27657                     editorcore.syncValue();
27658                 },
27659                 tabIndex:-1
27660             });
27661             
27662             cmenu.menu.items.push({
27663                 actiontype : 'all',
27664                 html: 'Remove All CSS Classes',
27665                 handler: function(a,b) {
27666                     
27667                     var c = Roo.get(editorcore.doc.body);
27668                     c.select('[class]').each(function(s) {
27669                         s.dom.removeAttribute('class');
27670                     });
27671                     editorcore.cleanWord();
27672                     editorcore.syncValue();
27673                 },
27674                 tabIndex:-1
27675             });
27676             
27677              cmenu.menu.items.push({
27678                 actiontype : 'tidy',
27679                 html: 'Tidy HTML Source',
27680                 handler: function(a,b) {
27681                     new Roo.htmleditor.Tidy(editorcore.doc.body);
27682                     editorcore.syncValue();
27683                 },
27684                 tabIndex:-1
27685             });
27686             
27687             
27688             tb.add(cmenu);
27689         }
27690          
27691         if (!this.disable.specialElements) {
27692             var semenu = {
27693                 text: "Other;",
27694                 cls: 'x-edit-none',
27695                 menu : {
27696                     items : []
27697                 }
27698             };
27699             for (var i =0; i < this.specialElements.length; i++) {
27700                 semenu.menu.items.push(
27701                     Roo.apply({ 
27702                         handler: function(a,b) {
27703                             editor.insertAtCursor(this.ihtml);
27704                         }
27705                     }, this.specialElements[i])
27706                 );
27707                     
27708             }
27709             
27710             tb.add(semenu);
27711             
27712             
27713         }
27714          
27715         
27716         if (this.btns) {
27717             for(var i =0; i< this.btns.length;i++) {
27718                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27719                 b.cls =  'x-edit-none';
27720                 
27721                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27722                     b.cls += ' x-init-enable';
27723                 }
27724                 
27725                 b.scope = editorcore;
27726                 tb.add(b);
27727             }
27728         
27729         }
27730         
27731         
27732         
27733         // disable everything...
27734         
27735         this.tb.items.each(function(item){
27736             
27737            if(
27738                 item.id != editorcore.frameId+ '-sourceedit' && 
27739                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27740             ){
27741                 
27742                 item.disable();
27743             }
27744         });
27745         this.rendered = true;
27746         
27747         // the all the btns;
27748         editor.on('editorevent', this.updateToolbar, this);
27749         // other toolbars need to implement this..
27750         //editor.on('editmodechange', this.updateToolbar, this);
27751     },
27752     
27753     
27754     relayBtnCmd : function(btn) {
27755         this.editorcore.relayCmd(btn.cmd);
27756     },
27757     // private used internally
27758     createLink : function(){
27759         //Roo.log("create link?");
27760         var ec = this.editorcore;
27761         var ar = ec.getAllAncestors();
27762         var n = false;
27763         for(var i = 0;i< ar.length;i++) {
27764             if (ar[i] && ar[i].nodeName == 'A') {
27765                 n = ar[i];
27766                 break;
27767             }
27768         }
27769         
27770         (function() {
27771             
27772             Roo.MessageBox.show({
27773                 title : "Add / Edit Link URL",
27774                 msg : "Enter the url for the link",
27775                 buttons: Roo.MessageBox.OKCANCEL,
27776                 fn: function(btn, url){
27777                     if (btn != 'ok') {
27778                         return;
27779                     }
27780                     if(url && url != 'http:/'+'/'){
27781                         if (n) {
27782                             n.setAttribute('href', url);
27783                         } else {
27784                             ec.relayCmd('createlink', url);
27785                         }
27786                     }
27787                 },
27788                 minWidth:250,
27789                 prompt:true,
27790                 //multiline: multiline,
27791                 modal : true,
27792                 value :  n  ? n.getAttribute('href') : '' 
27793             });
27794             
27795              
27796         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
27797         
27798     },
27799
27800     
27801     /**
27802      * Protected method that will not generally be called directly. It triggers
27803      * a toolbar update by reading the markup state of the current selection in the editor.
27804      */
27805     updateToolbar: function(){
27806
27807         if(!this.editorcore.activated){
27808             this.editor.onFirstFocus();
27809             return;
27810         }
27811
27812         var btns = this.tb.items.map, 
27813             doc = this.editorcore.doc,
27814             frameId = this.editorcore.frameId;
27815
27816         if(!this.disable.font && !Roo.isSafari){
27817             /*
27818             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27819             if(name != this.fontSelect.dom.value){
27820                 this.fontSelect.dom.value = name;
27821             }
27822             */
27823         }
27824         if(!this.disable.format){
27825             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27826             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27827             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27828             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27829         }
27830         if(!this.disable.alignments){
27831             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27832             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27833             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27834         }
27835         if(!Roo.isSafari && !this.disable.lists){
27836             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27837             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27838         }
27839         
27840         var ans = this.editorcore.getAllAncestors();
27841         if (this.formatCombo) {
27842             
27843             
27844             var store = this.formatCombo.store;
27845             this.formatCombo.setValue("");
27846             for (var i =0; i < ans.length;i++) {
27847                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27848                     // select it..
27849                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27850                     break;
27851                 }
27852             }
27853         }
27854         
27855         
27856         
27857         // hides menus... - so this cant be on a menu...
27858         Roo.menu.MenuMgr.hideAll();
27859
27860         //this.editorsyncValue();
27861     },
27862    
27863     
27864     createFontOptions : function(){
27865         var buf = [], fs = this.fontFamilies, ff, lc;
27866         
27867         
27868         
27869         for(var i = 0, len = fs.length; i< len; i++){
27870             ff = fs[i];
27871             lc = ff.toLowerCase();
27872             buf.push(
27873                 '<option value="',lc,'" style="font-family:',ff,';"',
27874                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27875                     ff,
27876                 '</option>'
27877             );
27878         }
27879         return buf.join('');
27880     },
27881     
27882     toggleSourceEdit : function(sourceEditMode){
27883         
27884         Roo.log("toolbar toogle");
27885         if(sourceEditMode === undefined){
27886             sourceEditMode = !this.sourceEditMode;
27887         }
27888         this.sourceEditMode = sourceEditMode === true;
27889         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27890         // just toggle the button?
27891         if(btn.pressed !== this.sourceEditMode){
27892             btn.toggle(this.sourceEditMode);
27893             return;
27894         }
27895         
27896         if(sourceEditMode){
27897             Roo.log("disabling buttons");
27898             this.tb.items.each(function(item){
27899                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27900                     item.disable();
27901                 }
27902             });
27903           
27904         }else{
27905             Roo.log("enabling buttons");
27906             if(this.editorcore.initialized){
27907                 this.tb.items.each(function(item){
27908                     item.enable();
27909                 });
27910                 // initialize 'blocks'
27911                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
27912                     Roo.htmleditor.Block.factory(e).updateElement(e);
27913                 },this);
27914             
27915             }
27916             
27917         }
27918         Roo.log("calling toggole on editor");
27919         // tell the editor that it's been pressed..
27920         this.editor.toggleSourceEdit(sourceEditMode);
27921        
27922     },
27923      /**
27924      * Object collection of toolbar tooltips for the buttons in the editor. The key
27925      * is the command id associated with that button and the value is a valid QuickTips object.
27926      * For example:
27927 <pre><code>
27928 {
27929     bold : {
27930         title: 'Bold (Ctrl+B)',
27931         text: 'Make the selected text bold.',
27932         cls: 'x-html-editor-tip'
27933     },
27934     italic : {
27935         title: 'Italic (Ctrl+I)',
27936         text: 'Make the selected text italic.',
27937         cls: 'x-html-editor-tip'
27938     },
27939     ...
27940 </code></pre>
27941     * @type Object
27942      */
27943     buttonTips : {
27944         bold : {
27945             title: 'Bold (Ctrl+B)',
27946             text: 'Make the selected text bold.',
27947             cls: 'x-html-editor-tip'
27948         },
27949         italic : {
27950             title: 'Italic (Ctrl+I)',
27951             text: 'Make the selected text italic.',
27952             cls: 'x-html-editor-tip'
27953         },
27954         underline : {
27955             title: 'Underline (Ctrl+U)',
27956             text: 'Underline the selected text.',
27957             cls: 'x-html-editor-tip'
27958         },
27959         strikethrough : {
27960             title: 'Strikethrough',
27961             text: 'Strikethrough the selected text.',
27962             cls: 'x-html-editor-tip'
27963         },
27964         increasefontsize : {
27965             title: 'Grow Text',
27966             text: 'Increase the font size.',
27967             cls: 'x-html-editor-tip'
27968         },
27969         decreasefontsize : {
27970             title: 'Shrink Text',
27971             text: 'Decrease the font size.',
27972             cls: 'x-html-editor-tip'
27973         },
27974         backcolor : {
27975             title: 'Text Highlight Color',
27976             text: 'Change the background color of the selected text.',
27977             cls: 'x-html-editor-tip'
27978         },
27979         forecolor : {
27980             title: 'Font Color',
27981             text: 'Change the color of the selected text.',
27982             cls: 'x-html-editor-tip'
27983         },
27984         justifyleft : {
27985             title: 'Align Text Left',
27986             text: 'Align text to the left.',
27987             cls: 'x-html-editor-tip'
27988         },
27989         justifycenter : {
27990             title: 'Center Text',
27991             text: 'Center text in the editor.',
27992             cls: 'x-html-editor-tip'
27993         },
27994         justifyright : {
27995             title: 'Align Text Right',
27996             text: 'Align text to the right.',
27997             cls: 'x-html-editor-tip'
27998         },
27999         insertunorderedlist : {
28000             title: 'Bullet List',
28001             text: 'Start a bulleted list.',
28002             cls: 'x-html-editor-tip'
28003         },
28004         insertorderedlist : {
28005             title: 'Numbered List',
28006             text: 'Start a numbered list.',
28007             cls: 'x-html-editor-tip'
28008         },
28009         createlink : {
28010             title: 'Hyperlink',
28011             text: 'Make the selected text a hyperlink.',
28012             cls: 'x-html-editor-tip'
28013         },
28014         sourceedit : {
28015             title: 'Source Edit',
28016             text: 'Switch to source editing mode.',
28017             cls: 'x-html-editor-tip'
28018         }
28019     },
28020     // private
28021     onDestroy : function(){
28022         if(this.rendered){
28023             
28024             this.tb.items.each(function(item){
28025                 if(item.menu){
28026                     item.menu.removeAll();
28027                     if(item.menu.el){
28028                         item.menu.el.destroy();
28029                     }
28030                 }
28031                 item.destroy();
28032             });
28033              
28034         }
28035     },
28036     onFirstFocus: function() {
28037         this.tb.items.each(function(item){
28038            item.enable();
28039         });
28040     }
28041 };
28042
28043
28044
28045
28046 // <script type="text/javascript">
28047 /*
28048  * Based on
28049  * Ext JS Library 1.1.1
28050  * Copyright(c) 2006-2007, Ext JS, LLC.
28051  *  
28052  
28053  */
28054
28055  
28056 /**
28057  * @class Roo.form.HtmlEditor.ToolbarContext
28058  * Context Toolbar
28059  * 
28060  * Usage:
28061  *
28062  new Roo.form.HtmlEditor({
28063     ....
28064     toolbars : [
28065         { xtype: 'ToolbarStandard', styles : {} }
28066         { xtype: 'ToolbarContext', disable : {} }
28067     ]
28068 })
28069
28070      
28071  * 
28072  * @config : {Object} disable List of elements to disable.. (not done yet.)
28073  * @config : {Object} styles  Map of styles available.
28074  * 
28075  */
28076
28077 Roo.form.HtmlEditor.ToolbarContext = function(config)
28078 {
28079     
28080     Roo.apply(this, config);
28081     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28082     // dont call parent... till later.
28083     this.styles = this.styles || {};
28084 }
28085
28086  
28087
28088 Roo.form.HtmlEditor.ToolbarContext.types = {
28089     'IMG' : [
28090         {
28091             name : 'width',
28092             title: "Width",
28093             width: 40
28094         },
28095         {
28096             name : 'height',
28097             title: "Height",
28098             width: 40
28099         },
28100         {
28101             name : 'align',
28102             title: "Align",
28103             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28104             width : 80
28105             
28106         },
28107         {
28108             name : 'border',
28109             title: "Border",
28110             width: 40
28111         },
28112         {
28113             name : 'alt',
28114             title: "Alt",
28115             width: 120
28116         },
28117         {
28118             name : 'src',
28119             title: "Src",
28120             width: 220
28121         }
28122         
28123     ],
28124     
28125     'FIGURE' : [
28126         {
28127             name : 'align',
28128             title: "Align",
28129             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28130             width : 80  
28131         }
28132     ],
28133     'A' : [
28134         {
28135             name : 'name',
28136             title: "Name",
28137             width: 50
28138         },
28139         {
28140             name : 'target',
28141             title: "Target",
28142             width: 120
28143         },
28144         {
28145             name : 'href',
28146             title: "Href",
28147             width: 220
28148         } // border?
28149         
28150     ],
28151     
28152     'INPUT' : [
28153         {
28154             name : 'name',
28155             title: "name",
28156             width: 120
28157         },
28158         {
28159             name : 'value',
28160             title: "Value",
28161             width: 120
28162         },
28163         {
28164             name : 'width',
28165             title: "Width",
28166             width: 40
28167         }
28168     ],
28169     'LABEL' : [
28170          {
28171             name : 'for',
28172             title: "For",
28173             width: 120
28174         }
28175     ],
28176     'TEXTAREA' : [
28177         {
28178             name : 'name',
28179             title: "name",
28180             width: 120
28181         },
28182         {
28183             name : 'rows',
28184             title: "Rows",
28185             width: 20
28186         },
28187         {
28188             name : 'cols',
28189             title: "Cols",
28190             width: 20
28191         }
28192     ],
28193     'SELECT' : [
28194         {
28195             name : 'name',
28196             title: "name",
28197             width: 120
28198         },
28199         {
28200             name : 'selectoptions',
28201             title: "Options",
28202             width: 200
28203         }
28204     ],
28205     
28206     // should we really allow this??
28207     // should this just be 
28208     'BODY' : [
28209         
28210         {
28211             name : 'title',
28212             title: "Title",
28213             width: 200,
28214             disabled : true
28215         }
28216     ],
28217  
28218     '*' : [
28219         // empty.
28220     ]
28221
28222 };
28223
28224 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28225 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28226
28227 Roo.form.HtmlEditor.ToolbarContext.options = {
28228         'font-family'  : [ 
28229                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28230                 [ 'Courier New', 'Courier New'],
28231                 [ 'Tahoma', 'Tahoma'],
28232                 [ 'Times New Roman,serif', 'Times'],
28233                 [ 'Verdana','Verdana' ]
28234         ]
28235 };
28236
28237 // fixme - these need to be configurable..
28238  
28239
28240 //Roo.form.HtmlEditor.ToolbarContext.types
28241
28242
28243 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28244     
28245     tb: false,
28246     
28247     rendered: false,
28248     
28249     editor : false,
28250     editorcore : false,
28251     /**
28252      * @cfg {Object} disable  List of toolbar elements to disable
28253          
28254      */
28255     disable : false,
28256     /**
28257      * @cfg {Object} styles List of styles 
28258      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28259      *
28260      * These must be defined in the page, so they get rendered correctly..
28261      * .headline { }
28262      * TD.underline { }
28263      * 
28264      */
28265     styles : false,
28266     
28267     options: false,
28268     
28269     toolbars : false,
28270     
28271     init : function(editor)
28272     {
28273         this.editor = editor;
28274         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28275         var editorcore = this.editorcore;
28276         
28277         var fid = editorcore.frameId;
28278         var etb = this;
28279         function btn(id, toggle, handler){
28280             var xid = fid + '-'+ id ;
28281             return {
28282                 id : xid,
28283                 cmd : id,
28284                 cls : 'x-btn-icon x-edit-'+id,
28285                 enableToggle:toggle !== false,
28286                 scope: editorcore, // was editor...
28287                 handler:handler||editorcore.relayBtnCmd,
28288                 clickEvent:'mousedown',
28289                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28290                 tabIndex:-1
28291             };
28292         }
28293         // create a new element.
28294         var wdiv = editor.wrap.createChild({
28295                 tag: 'div'
28296             }, editor.wrap.dom.firstChild.nextSibling, true);
28297         
28298         // can we do this more than once??
28299         
28300          // stop form submits
28301       
28302  
28303         // disable everything...
28304         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28305         this.toolbars = {};
28306         // block toolbars are built in updateToolbar when needed.
28307         for (var i in  ty) {
28308             
28309             this.toolbars[i] = this.buildToolbar(ty[i],i);
28310         }
28311         this.tb = this.toolbars.BODY;
28312         this.tb.el.show();
28313         this.buildFooter();
28314         this.footer.show();
28315         editor.on('hide', function( ) { this.footer.hide() }, this);
28316         editor.on('show', function( ) { this.footer.show() }, this);
28317         
28318          
28319         this.rendered = true;
28320         
28321         // the all the btns;
28322         editor.on('editorevent', this.updateToolbar, this);
28323         // other toolbars need to implement this..
28324         //editor.on('editmodechange', this.updateToolbar, this);
28325     },
28326     
28327     
28328     
28329     /**
28330      * Protected method that will not generally be called directly. It triggers
28331      * a toolbar update by reading the markup state of the current selection in the editor.
28332      *
28333      * Note you can force an update by calling on('editorevent', scope, false)
28334      */
28335     updateToolbar: function(editor ,ev, sel)
28336     {
28337         
28338         if (ev) {
28339             ev.stopEvent(); // se if we can stop this looping with mutiple events.
28340         }
28341         
28342         //Roo.log(ev);
28343         // capture mouse up - this is handy for selecting images..
28344         // perhaps should go somewhere else...
28345         if(!this.editorcore.activated){
28346              this.editor.onFirstFocus();
28347             return;
28348         }
28349         //Roo.log(ev ? ev.target : 'NOTARGET');
28350         
28351         
28352         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28353         // selectNode - might want to handle IE?
28354         
28355         
28356         
28357         if (ev &&
28358             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28359             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28360             // they have click on an image...
28361             // let's see if we can change the selection...
28362             sel = ev.target;
28363             
28364             // this triggers looping?
28365             //this.editorcore.selectNode(sel);
28366              
28367         }
28368         
28369         // this forces an id..
28370         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28371              e.classList.remove('roo-ed-selection');
28372         });
28373         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28374         //Roo.get(node).addClass('roo-ed-selection');
28375       
28376         //var updateFooter = sel ? false : true; 
28377         
28378         
28379         var ans = this.editorcore.getAllAncestors();
28380         
28381         // pick
28382         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28383         
28384         if (!sel) { 
28385             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28386             sel = sel ? sel : this.editorcore.doc.body;
28387             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28388             
28389         }
28390         
28391         var tn = sel.tagName.toUpperCase();
28392         var lastSel = this.tb.selectedNode;
28393         this.tb.selectedNode = sel;
28394         var left_label = tn;
28395         
28396         // ok see if we are editing a block?
28397         
28398         var db = false;
28399         // you are not actually selecting the block.
28400         if (sel && sel.hasAttribute('data-block')) {
28401             db = sel;
28402         } else if (sel && sel.closest('[data-block]')) {
28403             
28404             db = sel.closest('[data-block]');
28405             //var cepar = sel.closest('[contenteditable=true]');
28406             //if (db && cepar && cepar.tagName != 'BODY') {
28407             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28408             //}   
28409         }
28410         
28411         
28412         var block = false;
28413         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28414         if (db && this.editorcore.enableBlocks) {
28415             block = Roo.htmleditor.Block.factory(db);
28416             
28417             
28418             if (block) {
28419                  db.className = (
28420                         db.classList.length > 0  ? db.className + ' ' : ''
28421                     )  + 'roo-ed-selection';
28422                  
28423                  // since we removed it earlier... its not there..
28424                 tn = 'BLOCK.' + db.getAttribute('data-block');
28425                 
28426                 //this.editorcore.selectNode(db);
28427                 if (typeof(this.toolbars[tn]) == 'undefined') {
28428                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
28429                 }
28430                 this.toolbars[tn].selectedNode = db;
28431                 left_label = block.friendly_name;
28432                 ans = this.editorcore.getAllAncestors();
28433             }
28434             
28435                 
28436             
28437         }
28438         
28439         
28440         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28441             return; // no change?
28442         }
28443         
28444         
28445           
28446         this.tb.el.hide();
28447         ///console.log("show: " + tn);
28448         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28449         
28450         this.tb.el.show();
28451         // update name
28452         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
28453         
28454         
28455         // update attributes
28456         if (block && this.tb.fields) {
28457              
28458             this.tb.fields.each(function(e) {
28459                 e.setValue(block[e.name]);
28460             });
28461             
28462             
28463         } else  if (this.tb.fields && this.tb.selectedNode) {
28464             this.tb.fields.each( function(e) {
28465                 if (e.stylename) {
28466                     e.setValue(this.tb.selectedNode.style[e.stylename]);
28467                     return;
28468                 } 
28469                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28470             }, this);
28471             this.updateToolbarStyles(this.tb.selectedNode);  
28472         }
28473         
28474         
28475        
28476         Roo.menu.MenuMgr.hideAll();
28477
28478         
28479         
28480     
28481         // update the footer
28482         //
28483         this.updateFooter(ans);
28484              
28485     },
28486     
28487     updateToolbarStyles : function(sel)
28488     {
28489         var hasStyles = false;
28490         for(var i in this.styles) {
28491             hasStyles = true;
28492             break;
28493         }
28494         
28495         // update styles
28496         if (hasStyles && this.tb.hasStyles) { 
28497             var st = this.tb.fields.item(0);
28498             
28499             st.store.removeAll();
28500             var cn = sel.className.split(/\s+/);
28501             
28502             var avs = [];
28503             if (this.styles['*']) {
28504                 
28505                 Roo.each(this.styles['*'], function(v) {
28506                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28507                 });
28508             }
28509             if (this.styles[tn]) { 
28510                 Roo.each(this.styles[tn], function(v) {
28511                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28512                 });
28513             }
28514             
28515             st.store.loadData(avs);
28516             st.collapse();
28517             st.setValue(cn);
28518         }
28519     },
28520     
28521      
28522     updateFooter : function(ans)
28523     {
28524         var html = '';
28525         if (ans === false) {
28526             this.footDisp.dom.innerHTML = '';
28527             return;
28528         }
28529         
28530         this.footerEls = ans.reverse();
28531         Roo.each(this.footerEls, function(a,i) {
28532             if (!a) { return; }
28533             html += html.length ? ' &gt; '  :  '';
28534             
28535             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28536             
28537         });
28538        
28539         // 
28540         var sz = this.footDisp.up('td').getSize();
28541         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28542         this.footDisp.dom.style.marginLeft = '5px';
28543         
28544         this.footDisp.dom.style.overflow = 'hidden';
28545         
28546         this.footDisp.dom.innerHTML = html;
28547             
28548         
28549     },
28550    
28551        
28552     // private
28553     onDestroy : function(){
28554         if(this.rendered){
28555             
28556             this.tb.items.each(function(item){
28557                 if(item.menu){
28558                     item.menu.removeAll();
28559                     if(item.menu.el){
28560                         item.menu.el.destroy();
28561                     }
28562                 }
28563                 item.destroy();
28564             });
28565              
28566         }
28567     },
28568     onFirstFocus: function() {
28569         // need to do this for all the toolbars..
28570         this.tb.items.each(function(item){
28571            item.enable();
28572         });
28573     },
28574     buildToolbar: function(tlist, nm, friendly_name, block)
28575     {
28576         var editor = this.editor;
28577         var editorcore = this.editorcore;
28578          // create a new element.
28579         var wdiv = editor.wrap.createChild({
28580                 tag: 'div'
28581             }, editor.wrap.dom.firstChild.nextSibling, true);
28582         
28583        
28584         var tb = new Roo.Toolbar(wdiv);
28585         ///this.tb = tb; // << this sets the active toolbar..
28586         if (tlist === false && block) {
28587             tlist = block.contextMenu(this);
28588         }
28589         
28590         tb.hasStyles = false;
28591         tb.name = nm;
28592         
28593         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
28594         
28595         var styles = Array.from(this.styles);
28596         
28597         
28598         // styles...
28599         if (styles && styles.length) {
28600             tb.hasStyles = true;
28601             // this needs a multi-select checkbox...
28602             tb.addField( new Roo.form.ComboBox({
28603                 store: new Roo.data.SimpleStore({
28604                     id : 'val',
28605                     fields: ['val', 'selected'],
28606                     data : [] 
28607                 }),
28608                 name : '-roo-edit-className',
28609                 attrname : 'className',
28610                 displayField: 'val',
28611                 typeAhead: false,
28612                 mode: 'local',
28613                 editable : false,
28614                 triggerAction: 'all',
28615                 emptyText:'Select Style',
28616                 selectOnFocus:true,
28617                 width: 130,
28618                 listeners : {
28619                     'select': function(c, r, i) {
28620                         // initial support only for on class per el..
28621                         tb.selectedNode.className =  r ? r.get('val') : '';
28622                         editorcore.syncValue();
28623                     }
28624                 }
28625     
28626             }));
28627         }
28628         
28629         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28630         
28631         
28632         for (var i = 0; i < tlist.length; i++) {
28633             
28634             // newer versions will use xtype cfg to create menus.
28635             if (typeof(tlist[i].xtype) != 'undefined') {
28636                 
28637                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28638                 
28639                 
28640                 continue;
28641             }
28642             
28643             var item = tlist[i];
28644             tb.add(item.title + ":&nbsp;");
28645             
28646             
28647             //optname == used so you can configure the options available..
28648             var opts = item.opts ? item.opts : false;
28649             if (item.optname) { // use the b
28650                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28651            
28652             }
28653             
28654             if (opts) {
28655                 // opts == pulldown..
28656                 tb.addField( new Roo.form.ComboBox({
28657                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28658                         id : 'val',
28659                         fields: ['val', 'display'],
28660                         data : opts  
28661                     }),
28662                     name : '-roo-edit-' + tlist[i].name,
28663                     
28664                     attrname : tlist[i].name,
28665                     stylename : item.style ? item.style : false,
28666                     
28667                     displayField: item.displayField ? item.displayField : 'val',
28668                     valueField :  'val',
28669                     typeAhead: false,
28670                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
28671                     editable : false,
28672                     triggerAction: 'all',
28673                     emptyText:'Select',
28674                     selectOnFocus:true,
28675                     width: item.width ? item.width  : 130,
28676                     listeners : {
28677                         'select': function(c, r, i) {
28678                              
28679                             
28680                             if (c.stylename) {
28681                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28682                                 editorcore.syncValue();
28683                                 return;
28684                             }
28685                             if (r === false) {
28686                                 tb.selectedNode.removeAttribute(c.attrname);
28687                                 editorcore.syncValue();
28688                                 return;
28689                             }
28690                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28691                             editorcore.syncValue();
28692                         }
28693                     }
28694
28695                 }));
28696                 continue;
28697                     
28698                  
28699                 /*
28700                 tb.addField( new Roo.form.TextField({
28701                     name: i,
28702                     width: 100,
28703                     //allowBlank:false,
28704                     value: ''
28705                 }));
28706                 continue;
28707                 */
28708             }
28709             tb.addField( new Roo.form.TextField({
28710                 name: '-roo-edit-' + tlist[i].name,
28711                 attrname : tlist[i].name,
28712                 
28713                 width: item.width,
28714                 //allowBlank:true,
28715                 value: '',
28716                 listeners: {
28717                     'change' : function(f, nv, ov) {
28718                         
28719                          
28720                         tb.selectedNode.setAttribute(f.attrname, nv);
28721                         editorcore.syncValue();
28722                     }
28723                 }
28724             }));
28725              
28726         }
28727         
28728         var _this = this;
28729         var show_delete = !block || block.deleteTitle !== false;
28730         if(nm == 'BODY'){
28731             show_delete = false;
28732             tb.addSeparator();
28733         
28734             tb.addButton( {
28735                 text: 'Stylesheets',
28736
28737                 listeners : {
28738                     click : function ()
28739                     {
28740                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28741                     }
28742                 }
28743             });
28744         }
28745         
28746         tb.addFill();
28747         if (show_delete) {
28748             tb.addButton({
28749                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28750         
28751                 listeners : {
28752                     click : function ()
28753                     {
28754                         var sn = tb.selectedNode;
28755                         if (block) {
28756                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28757                             
28758                         }
28759                         if (!sn) {
28760                             return;
28761                         }
28762                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
28763                         if (sn.hasAttribute('data-block')) {
28764                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
28765                             sn.parentNode.removeChild(sn);
28766                             
28767                         } else if (sn && sn.tagName != 'BODY') {
28768                             // remove and keep parents.
28769                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
28770                             a.replaceTag(sn);
28771                         }
28772                         
28773                         
28774                         var range = editorcore.createRange();
28775             
28776                         range.setStart(stn,0);
28777                         range.setEnd(stn,0); 
28778                         var selection = editorcore.getSelection();
28779                         selection.removeAllRanges();
28780                         selection.addRange(range);
28781                         
28782                         
28783                         //_this.updateToolbar(null, null, pn);
28784                         _this.updateToolbar(null, null, null);
28785                         _this.updateFooter(false);
28786                         
28787                     }
28788                 }
28789                 
28790                         
28791                     
28792                 
28793             });
28794         }    
28795         
28796         tb.el.on('click', function(e){
28797             e.preventDefault(); // what does this do?
28798         });
28799         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28800         tb.el.hide();
28801         
28802         // dont need to disable them... as they will get hidden
28803         return tb;
28804          
28805         
28806     },
28807     buildFooter : function()
28808     {
28809         
28810         var fel = this.editor.wrap.createChild();
28811         this.footer = new Roo.Toolbar(fel);
28812         // toolbar has scrolly on left / right?
28813         var footDisp= new Roo.Toolbar.Fill();
28814         var _t = this;
28815         this.footer.add(
28816             {
28817                 text : '&lt;',
28818                 xtype: 'Button',
28819                 handler : function() {
28820                     _t.footDisp.scrollTo('left',0,true)
28821                 }
28822             }
28823         );
28824         this.footer.add( footDisp );
28825         this.footer.add( 
28826             {
28827                 text : '&gt;',
28828                 xtype: 'Button',
28829                 handler : function() {
28830                     // no animation..
28831                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28832                 }
28833             }
28834         );
28835         var fel = Roo.get(footDisp.el);
28836         fel.addClass('x-editor-context');
28837         this.footDispWrap = fel; 
28838         this.footDispWrap.overflow  = 'hidden';
28839         
28840         this.footDisp = fel.createChild();
28841         this.footDispWrap.on('click', this.onContextClick, this)
28842         
28843         
28844     },
28845     // when the footer contect changes
28846     onContextClick : function (ev,dom)
28847     {
28848         ev.preventDefault();
28849         var  cn = dom.className;
28850         //Roo.log(cn);
28851         if (!cn.match(/x-ed-loc-/)) {
28852             return;
28853         }
28854         var n = cn.split('-').pop();
28855         var ans = this.footerEls;
28856         var sel = ans[n];
28857         
28858         this.editorcore.selectNode(sel);
28859         
28860         
28861         this.updateToolbar(null, null, sel);
28862         
28863         
28864     }
28865     
28866     
28867     
28868     
28869     
28870 });
28871
28872
28873
28874
28875
28876 /*
28877  * Based on:
28878  * Ext JS Library 1.1.1
28879  * Copyright(c) 2006-2007, Ext JS, LLC.
28880  *
28881  * Originally Released Under LGPL - original licence link has changed is not relivant.
28882  *
28883  * Fork - LGPL
28884  * <script type="text/javascript">
28885  */
28886  
28887 /**
28888  * @class Roo.form.BasicForm
28889  * @extends Roo.util.Observable
28890  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28891  * @constructor
28892  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28893  * @param {Object} config Configuration options
28894  */
28895 Roo.form.BasicForm = function(el, config){
28896     this.allItems = [];
28897     this.childForms = [];
28898     Roo.apply(this, config);
28899     /*
28900      * The Roo.form.Field items in this form.
28901      * @type MixedCollection
28902      */
28903      
28904      
28905     this.items = new Roo.util.MixedCollection(false, function(o){
28906         return o.id || (o.id = Roo.id());
28907     });
28908     this.addEvents({
28909         /**
28910          * @event beforeaction
28911          * Fires before any action is performed. Return false to cancel the action.
28912          * @param {Form} this
28913          * @param {Action} action The action to be performed
28914          */
28915         beforeaction: true,
28916         /**
28917          * @event actionfailed
28918          * Fires when an action fails.
28919          * @param {Form} this
28920          * @param {Action} action The action that failed
28921          */
28922         actionfailed : true,
28923         /**
28924          * @event actioncomplete
28925          * Fires when an action is completed.
28926          * @param {Form} this
28927          * @param {Action} action The action that completed
28928          */
28929         actioncomplete : true
28930     });
28931     if(el){
28932         this.initEl(el);
28933     }
28934     Roo.form.BasicForm.superclass.constructor.call(this);
28935     
28936     Roo.form.BasicForm.popover.apply();
28937 };
28938
28939 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28940     /**
28941      * @cfg {String} method
28942      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28943      */
28944     /**
28945      * @cfg {DataReader} reader
28946      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28947      * This is optional as there is built-in support for processing JSON.
28948      */
28949     /**
28950      * @cfg {DataReader} errorReader
28951      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28952      * This is completely optional as there is built-in support for processing JSON.
28953      */
28954     /**
28955      * @cfg {String} url
28956      * The URL to use for form actions if one isn't supplied in the action options.
28957      */
28958     /**
28959      * @cfg {Boolean} fileUpload
28960      * Set to true if this form is a file upload.
28961      */
28962      
28963     /**
28964      * @cfg {Object} baseParams
28965      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28966      */
28967      /**
28968      
28969     /**
28970      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28971      */
28972     timeout: 30,
28973
28974     // private
28975     activeAction : null,
28976
28977     /**
28978      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28979      * or setValues() data instead of when the form was first created.
28980      */
28981     trackResetOnLoad : false,
28982     
28983     
28984     /**
28985      * childForms - used for multi-tab forms
28986      * @type {Array}
28987      */
28988     childForms : false,
28989     
28990     /**
28991      * allItems - full list of fields.
28992      * @type {Array}
28993      */
28994     allItems : false,
28995     
28996     /**
28997      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28998      * element by passing it or its id or mask the form itself by passing in true.
28999      * @type Mixed
29000      */
29001     waitMsgTarget : false,
29002     
29003     /**
29004      * @type Boolean
29005      */
29006     disableMask : false,
29007     
29008     /**
29009      * @cfg {Boolean} errorMask (true|false) default false
29010      */
29011     errorMask : false,
29012     
29013     /**
29014      * @cfg {Number} maskOffset Default 100
29015      */
29016     maskOffset : 100,
29017
29018     // private
29019     initEl : function(el){
29020         this.el = Roo.get(el);
29021         this.id = this.el.id || Roo.id();
29022         this.el.on('submit', this.onSubmit, this);
29023         this.el.addClass('x-form');
29024     },
29025
29026     // private
29027     onSubmit : function(e){
29028         e.stopEvent();
29029     },
29030
29031     /**
29032      * Returns true if client-side validation on the form is successful.
29033      * @return Boolean
29034      */
29035     isValid : function(){
29036         var valid = true;
29037         var target = false;
29038         this.items.each(function(f){
29039             if(f.validate()){
29040                 return;
29041             }
29042             
29043             valid = false;
29044                 
29045             if(!target && f.el.isVisible(true)){
29046                 target = f;
29047             }
29048         });
29049         
29050         if(this.errorMask && !valid){
29051             Roo.form.BasicForm.popover.mask(this, target);
29052         }
29053         
29054         return valid;
29055     },
29056     /**
29057      * Returns array of invalid form fields.
29058      * @return Array
29059      */
29060     
29061     invalidFields : function()
29062     {
29063         var ret = [];
29064         this.items.each(function(f){
29065             if(f.validate()){
29066                 return;
29067             }
29068             ret.push(f);
29069             
29070         });
29071         
29072         return ret;
29073     },
29074     
29075     
29076     /**
29077      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
29078      * @return Boolean
29079      */
29080     isDirty : function(){
29081         var dirty = false;
29082         this.items.each(function(f){
29083            if(f.isDirty()){
29084                dirty = true;
29085                return false;
29086            }
29087         });
29088         return dirty;
29089     },
29090     
29091     /**
29092      * Returns true if any fields in this form have changed since their original load. (New version)
29093      * @return Boolean
29094      */
29095     
29096     hasChanged : function()
29097     {
29098         var dirty = false;
29099         this.items.each(function(f){
29100            if(f.hasChanged()){
29101                dirty = true;
29102                return false;
29103            }
29104         });
29105         return dirty;
29106         
29107     },
29108     /**
29109      * Resets all hasChanged to 'false' -
29110      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29111      * So hasChanged storage is only to be used for this purpose
29112      * @return Boolean
29113      */
29114     resetHasChanged : function()
29115     {
29116         this.items.each(function(f){
29117            f.resetHasChanged();
29118         });
29119         
29120     },
29121     
29122     
29123     /**
29124      * Performs a predefined action (submit or load) or custom actions you define on this form.
29125      * @param {String} actionName The name of the action type
29126      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
29127      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29128      * accept other config options):
29129      * <pre>
29130 Property          Type             Description
29131 ----------------  ---------------  ----------------------------------------------------------------------------------
29132 url               String           The url for the action (defaults to the form's url)
29133 method            String           The form method to use (defaults to the form's method, or POST if not defined)
29134 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
29135 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
29136                                    validate the form on the client (defaults to false)
29137      * </pre>
29138      * @return {BasicForm} this
29139      */
29140     doAction : function(action, options){
29141         if(typeof action == 'string'){
29142             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29143         }
29144         if(this.fireEvent('beforeaction', this, action) !== false){
29145             this.beforeAction(action);
29146             action.run.defer(100, action);
29147         }
29148         return this;
29149     },
29150
29151     /**
29152      * Shortcut to do a submit action.
29153      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29154      * @return {BasicForm} this
29155      */
29156     submit : function(options){
29157         this.doAction('submit', options);
29158         return this;
29159     },
29160
29161     /**
29162      * Shortcut to do a load action.
29163      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29164      * @return {BasicForm} this
29165      */
29166     load : function(options){
29167         this.doAction('load', options);
29168         return this;
29169     },
29170
29171     /**
29172      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29173      * @param {Record} record The record to edit
29174      * @return {BasicForm} this
29175      */
29176     updateRecord : function(record){
29177         record.beginEdit();
29178         var fs = record.fields;
29179         fs.each(function(f){
29180             var field = this.findField(f.name);
29181             if(field){
29182                 record.set(f.name, field.getValue());
29183             }
29184         }, this);
29185         record.endEdit();
29186         return this;
29187     },
29188
29189     /**
29190      * Loads an Roo.data.Record into this form.
29191      * @param {Record} record The record to load
29192      * @return {BasicForm} this
29193      */
29194     loadRecord : function(record){
29195         this.setValues(record.data);
29196         return this;
29197     },
29198
29199     // private
29200     beforeAction : function(action){
29201         var o = action.options;
29202         
29203         if(!this.disableMask) {
29204             if(this.waitMsgTarget === true){
29205                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29206             }else if(this.waitMsgTarget){
29207                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29208                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29209             }else {
29210                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29211             }
29212         }
29213         
29214          
29215     },
29216
29217     // private
29218     afterAction : function(action, success){
29219         this.activeAction = null;
29220         var o = action.options;
29221         
29222         if(!this.disableMask) {
29223             if(this.waitMsgTarget === true){
29224                 this.el.unmask();
29225             }else if(this.waitMsgTarget){
29226                 this.waitMsgTarget.unmask();
29227             }else{
29228                 Roo.MessageBox.updateProgress(1);
29229                 Roo.MessageBox.hide();
29230             }
29231         }
29232         
29233         if(success){
29234             if(o.reset){
29235                 this.reset();
29236             }
29237             Roo.callback(o.success, o.scope, [this, action]);
29238             this.fireEvent('actioncomplete', this, action);
29239             
29240         }else{
29241             
29242             // failure condition..
29243             // we have a scenario where updates need confirming.
29244             // eg. if a locking scenario exists..
29245             // we look for { errors : { needs_confirm : true }} in the response.
29246             if (
29247                 (typeof(action.result) != 'undefined')  &&
29248                 (typeof(action.result.errors) != 'undefined')  &&
29249                 (typeof(action.result.errors.needs_confirm) != 'undefined')
29250            ){
29251                 var _t = this;
29252                 Roo.MessageBox.confirm(
29253                     "Change requires confirmation",
29254                     action.result.errorMsg,
29255                     function(r) {
29256                         if (r != 'yes') {
29257                             return;
29258                         }
29259                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
29260                     }
29261                     
29262                 );
29263                 
29264                 
29265                 
29266                 return;
29267             }
29268             
29269             Roo.callback(o.failure, o.scope, [this, action]);
29270             // show an error message if no failed handler is set..
29271             if (!this.hasListener('actionfailed')) {
29272                 Roo.MessageBox.alert("Error",
29273                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29274                         action.result.errorMsg :
29275                         "Saving Failed, please check your entries or try again"
29276                 );
29277             }
29278             
29279             this.fireEvent('actionfailed', this, action);
29280         }
29281         
29282     },
29283
29284     /**
29285      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29286      * @param {String} id The value to search for
29287      * @return Field
29288      */
29289     findField : function(id){
29290         var field = this.items.get(id);
29291         if(!field){
29292             this.items.each(function(f){
29293                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29294                     field = f;
29295                     return false;
29296                 }
29297             });
29298         }
29299         return field || null;
29300     },
29301
29302     /**
29303      * Add a secondary form to this one, 
29304      * Used to provide tabbed forms. One form is primary, with hidden values 
29305      * which mirror the elements from the other forms.
29306      * 
29307      * @param {Roo.form.Form} form to add.
29308      * 
29309      */
29310     addForm : function(form)
29311     {
29312        
29313         if (this.childForms.indexOf(form) > -1) {
29314             // already added..
29315             return;
29316         }
29317         this.childForms.push(form);
29318         var n = '';
29319         Roo.each(form.allItems, function (fe) {
29320             
29321             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29322             if (this.findField(n)) { // already added..
29323                 return;
29324             }
29325             var add = new Roo.form.Hidden({
29326                 name : n
29327             });
29328             add.render(this.el);
29329             
29330             this.add( add );
29331         }, this);
29332         
29333     },
29334     /**
29335      * Mark fields in this form invalid in bulk.
29336      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29337      * @return {BasicForm} this
29338      */
29339     markInvalid : function(errors){
29340         if(errors instanceof Array){
29341             for(var i = 0, len = errors.length; i < len; i++){
29342                 var fieldError = errors[i];
29343                 var f = this.findField(fieldError.id);
29344                 if(f){
29345                     f.markInvalid(fieldError.msg);
29346                 }
29347             }
29348         }else{
29349             var field, id;
29350             for(id in errors){
29351                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29352                     field.markInvalid(errors[id]);
29353                 }
29354             }
29355         }
29356         Roo.each(this.childForms || [], function (f) {
29357             f.markInvalid(errors);
29358         });
29359         
29360         return this;
29361     },
29362
29363     /**
29364      * Set values for fields in this form in bulk.
29365      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29366      * @return {BasicForm} this
29367      */
29368     setValues : function(values){
29369         if(values instanceof Array){ // array of objects
29370             for(var i = 0, len = values.length; i < len; i++){
29371                 var v = values[i];
29372                 var f = this.findField(v.id);
29373                 if(f){
29374                     f.setValue(v.value);
29375                     if(this.trackResetOnLoad){
29376                         f.originalValue = f.getValue();
29377                     }
29378                 }
29379             }
29380         }else{ // object hash
29381             var field, id;
29382             for(id in values){
29383                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29384                     
29385                     if (field.setFromData && 
29386                         field.valueField && 
29387                         field.displayField &&
29388                         // combos' with local stores can 
29389                         // be queried via setValue()
29390                         // to set their value..
29391                         (field.store && !field.store.isLocal)
29392                         ) {
29393                         // it's a combo
29394                         var sd = { };
29395                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29396                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29397                         field.setFromData(sd);
29398                         
29399                     } else {
29400                         field.setValue(values[id]);
29401                     }
29402                     
29403                     
29404                     if(this.trackResetOnLoad){
29405                         field.originalValue = field.getValue();
29406                     }
29407                 }
29408             }
29409         }
29410         this.resetHasChanged();
29411         
29412         
29413         Roo.each(this.childForms || [], function (f) {
29414             f.setValues(values);
29415             f.resetHasChanged();
29416         });
29417                 
29418         return this;
29419     },
29420  
29421     /**
29422      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29423      * they are returned as an array.
29424      * @param {Boolean} asString
29425      * @return {Object}
29426      */
29427     getValues : function(asString)
29428     {
29429         if (this.childForms) {
29430             // copy values from the child forms
29431             Roo.each(this.childForms, function (f) {
29432                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29433             }, this);
29434         }
29435         
29436         // use formdata
29437         if (typeof(FormData) != 'undefined' && asString !== true) {
29438             // this relies on a 'recent' version of chrome apparently...
29439             try {
29440                 var fd = (new FormData(this.el.dom)).entries();
29441                 var ret = {};
29442                 var ent = fd.next();
29443                 while (!ent.done) {
29444                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29445                     ent = fd.next();
29446                 };
29447                 return ret;
29448             } catch(e) {
29449                 
29450             }
29451             
29452         }
29453         
29454         
29455         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29456         if(asString === true){
29457             return fs;
29458         }
29459         return Roo.urlDecode(fs);
29460     },
29461     
29462     /**
29463      * Returns the fields in this form as an object with key/value pairs. 
29464      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29465      * Normally this will not return readOnly data 
29466      * @param {Boolean} with_readonly return readonly field data.
29467      * @return {Object}
29468      */
29469     getFieldValues : function(with_readonly)
29470     {
29471         if (this.childForms) {
29472             // copy values from the child forms
29473             // should this call getFieldValues - probably not as we do not currently copy
29474             // hidden fields when we generate..
29475             Roo.each(this.childForms, function (f) {
29476                 this.setValues(f.getFieldValues());
29477             }, this);
29478         }
29479         
29480         var ret = {};
29481         this.items.each(function(f){
29482             
29483             if (f.readOnly && with_readonly !== true) {
29484                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29485                         // if a subform contains a copy of them.
29486                         // if you have subforms with the same editable data, you will need to copy the data back
29487                         // and forth.
29488             }
29489             
29490             if (!f.getName()) {
29491                 return;
29492             }
29493             var v = f.getValue();
29494             if (f.inputType =='radio') {
29495                 if (typeof(ret[f.getName()]) == 'undefined') {
29496                     ret[f.getName()] = ''; // empty..
29497                 }
29498                 
29499                 if (!f.el.dom.checked) {
29500                     return;
29501                     
29502                 }
29503                 v = f.el.dom.value;
29504                 
29505             }
29506             
29507             // not sure if this supported any more..
29508             if ((typeof(v) == 'object') && f.getRawValue) {
29509                 v = f.getRawValue() ; // dates..
29510             }
29511             // combo boxes where name != hiddenName...
29512             if (f.name != f.getName()) {
29513                 ret[f.name] = f.getRawValue();
29514             }
29515             ret[f.getName()] = v;
29516         });
29517         
29518         return ret;
29519     },
29520
29521     /**
29522      * Clears all invalid messages in this form.
29523      * @return {BasicForm} this
29524      */
29525     clearInvalid : function(){
29526         this.items.each(function(f){
29527            f.clearInvalid();
29528         });
29529         
29530         Roo.each(this.childForms || [], function (f) {
29531             f.clearInvalid();
29532         });
29533         
29534         
29535         return this;
29536     },
29537
29538     /**
29539      * Resets this form.
29540      * @return {BasicForm} this
29541      */
29542     reset : function(){
29543         this.items.each(function(f){
29544             f.reset();
29545         });
29546         
29547         Roo.each(this.childForms || [], function (f) {
29548             f.reset();
29549         });
29550         this.resetHasChanged();
29551         
29552         return this;
29553     },
29554
29555     /**
29556      * Add Roo.form components to this form.
29557      * @param {Field} field1
29558      * @param {Field} field2 (optional)
29559      * @param {Field} etc (optional)
29560      * @return {BasicForm} this
29561      */
29562     add : function(){
29563         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29564         return this;
29565     },
29566
29567
29568     /**
29569      * Removes a field from the items collection (does NOT remove its markup).
29570      * @param {Field} field
29571      * @return {BasicForm} this
29572      */
29573     remove : function(field){
29574         this.items.remove(field);
29575         return this;
29576     },
29577
29578     /**
29579      * Looks at the fields in this form, checks them for an id attribute,
29580      * and calls applyTo on the existing dom element with that id.
29581      * @return {BasicForm} this
29582      */
29583     render : function(){
29584         this.items.each(function(f){
29585             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29586                 f.applyTo(f.id);
29587             }
29588         });
29589         return this;
29590     },
29591
29592     /**
29593      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29594      * @param {Object} values
29595      * @return {BasicForm} this
29596      */
29597     applyToFields : function(o){
29598         this.items.each(function(f){
29599            Roo.apply(f, o);
29600         });
29601         return this;
29602     },
29603
29604     /**
29605      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29606      * @param {Object} values
29607      * @return {BasicForm} this
29608      */
29609     applyIfToFields : function(o){
29610         this.items.each(function(f){
29611            Roo.applyIf(f, o);
29612         });
29613         return this;
29614     }
29615 });
29616
29617 // back compat
29618 Roo.BasicForm = Roo.form.BasicForm;
29619
29620 Roo.apply(Roo.form.BasicForm, {
29621     
29622     popover : {
29623         
29624         padding : 5,
29625         
29626         isApplied : false,
29627         
29628         isMasked : false,
29629         
29630         form : false,
29631         
29632         target : false,
29633         
29634         intervalID : false,
29635         
29636         maskEl : false,
29637         
29638         apply : function()
29639         {
29640             if(this.isApplied){
29641                 return;
29642             }
29643             
29644             this.maskEl = {
29645                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29646                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29647                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29648                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29649             };
29650             
29651             this.maskEl.top.enableDisplayMode("block");
29652             this.maskEl.left.enableDisplayMode("block");
29653             this.maskEl.bottom.enableDisplayMode("block");
29654             this.maskEl.right.enableDisplayMode("block");
29655             
29656             Roo.get(document.body).on('click', function(){
29657                 this.unmask();
29658             }, this);
29659             
29660             Roo.get(document.body).on('touchstart', function(){
29661                 this.unmask();
29662             }, this);
29663             
29664             this.isApplied = true
29665         },
29666         
29667         mask : function(form, target)
29668         {
29669             this.form = form;
29670             
29671             this.target = target;
29672             
29673             if(!this.form.errorMask || !target.el){
29674                 return;
29675             }
29676             
29677             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29678             
29679             var ot = this.target.el.calcOffsetsTo(scrollable);
29680             
29681             var scrollTo = ot[1] - this.form.maskOffset;
29682             
29683             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29684             
29685             scrollable.scrollTo('top', scrollTo);
29686             
29687             var el = this.target.wrap || this.target.el;
29688             
29689             var box = el.getBox();
29690             
29691             this.maskEl.top.setStyle('position', 'absolute');
29692             this.maskEl.top.setStyle('z-index', 10000);
29693             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29694             this.maskEl.top.setLeft(0);
29695             this.maskEl.top.setTop(0);
29696             this.maskEl.top.show();
29697             
29698             this.maskEl.left.setStyle('position', 'absolute');
29699             this.maskEl.left.setStyle('z-index', 10000);
29700             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29701             this.maskEl.left.setLeft(0);
29702             this.maskEl.left.setTop(box.y - this.padding);
29703             this.maskEl.left.show();
29704
29705             this.maskEl.bottom.setStyle('position', 'absolute');
29706             this.maskEl.bottom.setStyle('z-index', 10000);
29707             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29708             this.maskEl.bottom.setLeft(0);
29709             this.maskEl.bottom.setTop(box.bottom + this.padding);
29710             this.maskEl.bottom.show();
29711
29712             this.maskEl.right.setStyle('position', 'absolute');
29713             this.maskEl.right.setStyle('z-index', 10000);
29714             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29715             this.maskEl.right.setLeft(box.right + this.padding);
29716             this.maskEl.right.setTop(box.y - this.padding);
29717             this.maskEl.right.show();
29718
29719             this.intervalID = window.setInterval(function() {
29720                 Roo.form.BasicForm.popover.unmask();
29721             }, 10000);
29722
29723             window.onwheel = function(){ return false;};
29724             
29725             (function(){ this.isMasked = true; }).defer(500, this);
29726             
29727         },
29728         
29729         unmask : function()
29730         {
29731             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29732                 return;
29733             }
29734             
29735             this.maskEl.top.setStyle('position', 'absolute');
29736             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29737             this.maskEl.top.hide();
29738
29739             this.maskEl.left.setStyle('position', 'absolute');
29740             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29741             this.maskEl.left.hide();
29742
29743             this.maskEl.bottom.setStyle('position', 'absolute');
29744             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29745             this.maskEl.bottom.hide();
29746
29747             this.maskEl.right.setStyle('position', 'absolute');
29748             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29749             this.maskEl.right.hide();
29750             
29751             window.onwheel = function(){ return true;};
29752             
29753             if(this.intervalID){
29754                 window.clearInterval(this.intervalID);
29755                 this.intervalID = false;
29756             }
29757             
29758             this.isMasked = false;
29759             
29760         }
29761         
29762     }
29763     
29764 });/*
29765  * Based on:
29766  * Ext JS Library 1.1.1
29767  * Copyright(c) 2006-2007, Ext JS, LLC.
29768  *
29769  * Originally Released Under LGPL - original licence link has changed is not relivant.
29770  *
29771  * Fork - LGPL
29772  * <script type="text/javascript">
29773  */
29774
29775 /**
29776  * @class Roo.form.Form
29777  * @extends Roo.form.BasicForm
29778  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29779  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29780  * @constructor
29781  * @param {Object} config Configuration options
29782  */
29783 Roo.form.Form = function(config){
29784     var xitems =  [];
29785     if (config.items) {
29786         xitems = config.items;
29787         delete config.items;
29788     }
29789    
29790     
29791     Roo.form.Form.superclass.constructor.call(this, null, config);
29792     this.url = this.url || this.action;
29793     if(!this.root){
29794         this.root = new Roo.form.Layout(Roo.applyIf({
29795             id: Roo.id()
29796         }, config));
29797     }
29798     this.active = this.root;
29799     /**
29800      * Array of all the buttons that have been added to this form via {@link addButton}
29801      * @type Array
29802      */
29803     this.buttons = [];
29804     this.allItems = [];
29805     this.addEvents({
29806         /**
29807          * @event clientvalidation
29808          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29809          * @param {Form} this
29810          * @param {Boolean} valid true if the form has passed client-side validation
29811          */
29812         clientvalidation: true,
29813         /**
29814          * @event rendered
29815          * Fires when the form is rendered
29816          * @param {Roo.form.Form} form
29817          */
29818         rendered : true
29819     });
29820     
29821     if (this.progressUrl) {
29822             // push a hidden field onto the list of fields..
29823             this.addxtype( {
29824                     xns: Roo.form, 
29825                     xtype : 'Hidden', 
29826                     name : 'UPLOAD_IDENTIFIER' 
29827             });
29828         }
29829         
29830     
29831     Roo.each(xitems, this.addxtype, this);
29832     
29833 };
29834
29835 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29836      /**
29837      * @cfg {Roo.Button} buttons[] buttons at bottom of form
29838      */
29839     
29840     /**
29841      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29842      */
29843     /**
29844      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29845      */
29846     /**
29847      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29848      */
29849     buttonAlign:'center',
29850
29851     /**
29852      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29853      */
29854     minButtonWidth:75,
29855
29856     /**
29857      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29858      * This property cascades to child containers if not set.
29859      */
29860     labelAlign:'left',
29861
29862     /**
29863      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29864      * fires a looping event with that state. This is required to bind buttons to the valid
29865      * state using the config value formBind:true on the button.
29866      */
29867     monitorValid : false,
29868
29869     /**
29870      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29871      */
29872     monitorPoll : 200,
29873     
29874     /**
29875      * @cfg {String} progressUrl - Url to return progress data 
29876      */
29877     
29878     progressUrl : false,
29879     /**
29880      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
29881      * sending a formdata with extra parameters - eg uploaded elements.
29882      */
29883     
29884     formData : false,
29885     
29886     /**
29887      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29888      * fields are added and the column is closed. If no fields are passed the column remains open
29889      * until end() is called.
29890      * @param {Object} config The config to pass to the column
29891      * @param {Field} field1 (optional)
29892      * @param {Field} field2 (optional)
29893      * @param {Field} etc (optional)
29894      * @return Column The column container object
29895      */
29896     column : function(c){
29897         var col = new Roo.form.Column(c);
29898         this.start(col);
29899         if(arguments.length > 1){ // duplicate code required because of Opera
29900             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29901             this.end();
29902         }
29903         return col;
29904     },
29905
29906     /**
29907      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29908      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29909      * until end() is called.
29910      * @param {Object} config The config to pass to the fieldset
29911      * @param {Field} field1 (optional)
29912      * @param {Field} field2 (optional)
29913      * @param {Field} etc (optional)
29914      * @return FieldSet The fieldset container object
29915      */
29916     fieldset : function(c){
29917         var fs = new Roo.form.FieldSet(c);
29918         this.start(fs);
29919         if(arguments.length > 1){ // duplicate code required because of Opera
29920             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29921             this.end();
29922         }
29923         return fs;
29924     },
29925
29926     /**
29927      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29928      * fields are added and the container is closed. If no fields are passed the container remains open
29929      * until end() is called.
29930      * @param {Object} config The config to pass to the Layout
29931      * @param {Field} field1 (optional)
29932      * @param {Field} field2 (optional)
29933      * @param {Field} etc (optional)
29934      * @return Layout The container object
29935      */
29936     container : function(c){
29937         var l = new Roo.form.Layout(c);
29938         this.start(l);
29939         if(arguments.length > 1){ // duplicate code required because of Opera
29940             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29941             this.end();
29942         }
29943         return l;
29944     },
29945
29946     /**
29947      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29948      * @param {Object} container A Roo.form.Layout or subclass of Layout
29949      * @return {Form} this
29950      */
29951     start : function(c){
29952         // cascade label info
29953         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29954         this.active.stack.push(c);
29955         c.ownerCt = this.active;
29956         this.active = c;
29957         return this;
29958     },
29959
29960     /**
29961      * Closes the current open container
29962      * @return {Form} this
29963      */
29964     end : function(){
29965         if(this.active == this.root){
29966             return this;
29967         }
29968         this.active = this.active.ownerCt;
29969         return this;
29970     },
29971
29972     /**
29973      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29974      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29975      * as the label of the field.
29976      * @param {Field} field1
29977      * @param {Field} field2 (optional)
29978      * @param {Field} etc. (optional)
29979      * @return {Form} this
29980      */
29981     add : function(){
29982         this.active.stack.push.apply(this.active.stack, arguments);
29983         this.allItems.push.apply(this.allItems,arguments);
29984         var r = [];
29985         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29986             if(a[i].isFormField){
29987                 r.push(a[i]);
29988             }
29989         }
29990         if(r.length > 0){
29991             Roo.form.Form.superclass.add.apply(this, r);
29992         }
29993         return this;
29994     },
29995     
29996
29997     
29998     
29999     
30000      /**
30001      * Find any element that has been added to a form, using it's ID or name
30002      * This can include framesets, columns etc. along with regular fields..
30003      * @param {String} id - id or name to find.
30004      
30005      * @return {Element} e - or false if nothing found.
30006      */
30007     findbyId : function(id)
30008     {
30009         var ret = false;
30010         if (!id) {
30011             return ret;
30012         }
30013         Roo.each(this.allItems, function(f){
30014             if (f.id == id || f.name == id ){
30015                 ret = f;
30016                 return false;
30017             }
30018         });
30019         return ret;
30020     },
30021
30022     
30023     
30024     /**
30025      * Render this form into the passed container. This should only be called once!
30026      * @param {String/HTMLElement/Element} container The element this component should be rendered into
30027      * @return {Form} this
30028      */
30029     render : function(ct)
30030     {
30031         
30032         
30033         
30034         ct = Roo.get(ct);
30035         var o = this.autoCreate || {
30036             tag: 'form',
30037             method : this.method || 'POST',
30038             id : this.id || Roo.id()
30039         };
30040         this.initEl(ct.createChild(o));
30041
30042         this.root.render(this.el);
30043         
30044        
30045              
30046         this.items.each(function(f){
30047             f.render('x-form-el-'+f.id);
30048         });
30049
30050         if(this.buttons.length > 0){
30051             // tables are required to maintain order and for correct IE layout
30052             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30053                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30054                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30055             }}, null, true);
30056             var tr = tb.getElementsByTagName('tr')[0];
30057             for(var i = 0, len = this.buttons.length; i < len; i++) {
30058                 var b = this.buttons[i];
30059                 var td = document.createElement('td');
30060                 td.className = 'x-form-btn-td';
30061                 b.render(tr.appendChild(td));
30062             }
30063         }
30064         if(this.monitorValid){ // initialize after render
30065             this.startMonitoring();
30066         }
30067         this.fireEvent('rendered', this);
30068         return this;
30069     },
30070
30071     /**
30072      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30073      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30074      * object or a valid Roo.DomHelper element config
30075      * @param {Function} handler The function called when the button is clicked
30076      * @param {Object} scope (optional) The scope of the handler function
30077      * @return {Roo.Button}
30078      */
30079     addButton : function(config, handler, scope){
30080         var bc = {
30081             handler: handler,
30082             scope: scope,
30083             minWidth: this.minButtonWidth,
30084             hideParent:true
30085         };
30086         if(typeof config == "string"){
30087             bc.text = config;
30088         }else{
30089             Roo.apply(bc, config);
30090         }
30091         var btn = new Roo.Button(null, bc);
30092         this.buttons.push(btn);
30093         return btn;
30094     },
30095
30096      /**
30097      * Adds a series of form elements (using the xtype property as the factory method.
30098      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30099      * @param {Object} config 
30100      */
30101     
30102     addxtype : function()
30103     {
30104         var ar = Array.prototype.slice.call(arguments, 0);
30105         var ret = false;
30106         for(var i = 0; i < ar.length; i++) {
30107             if (!ar[i]) {
30108                 continue; // skip -- if this happends something invalid got sent, we 
30109                 // should ignore it, as basically that interface element will not show up
30110                 // and that should be pretty obvious!!
30111             }
30112             
30113             if (Roo.form[ar[i].xtype]) {
30114                 ar[i].form = this;
30115                 var fe = Roo.factory(ar[i], Roo.form);
30116                 if (!ret) {
30117                     ret = fe;
30118                 }
30119                 fe.form = this;
30120                 if (fe.store) {
30121                     fe.store.form = this;
30122                 }
30123                 if (fe.isLayout) {  
30124                          
30125                     this.start(fe);
30126                     this.allItems.push(fe);
30127                     if (fe.items && fe.addxtype) {
30128                         fe.addxtype.apply(fe, fe.items);
30129                         delete fe.items;
30130                     }
30131                      this.end();
30132                     continue;
30133                 }
30134                 
30135                 
30136                  
30137                 this.add(fe);
30138               //  console.log('adding ' + ar[i].xtype);
30139             }
30140             if (ar[i].xtype == 'Button') {  
30141                 //console.log('adding button');
30142                 //console.log(ar[i]);
30143                 this.addButton(ar[i]);
30144                 this.allItems.push(fe);
30145                 continue;
30146             }
30147             
30148             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30149                 alert('end is not supported on xtype any more, use items');
30150             //    this.end();
30151             //    //console.log('adding end');
30152             }
30153             
30154         }
30155         return ret;
30156     },
30157     
30158     /**
30159      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30160      * option "monitorValid"
30161      */
30162     startMonitoring : function(){
30163         if(!this.bound){
30164             this.bound = true;
30165             Roo.TaskMgr.start({
30166                 run : this.bindHandler,
30167                 interval : this.monitorPoll || 200,
30168                 scope: this
30169             });
30170         }
30171     },
30172
30173     /**
30174      * Stops monitoring of the valid state of this form
30175      */
30176     stopMonitoring : function(){
30177         this.bound = false;
30178     },
30179
30180     // private
30181     bindHandler : function(){
30182         if(!this.bound){
30183             return false; // stops binding
30184         }
30185         var valid = true;
30186         this.items.each(function(f){
30187             if(!f.isValid(true)){
30188                 valid = false;
30189                 return false;
30190             }
30191         });
30192         for(var i = 0, len = this.buttons.length; i < len; i++){
30193             var btn = this.buttons[i];
30194             if(btn.formBind === true && btn.disabled === valid){
30195                 btn.setDisabled(!valid);
30196             }
30197         }
30198         this.fireEvent('clientvalidation', this, valid);
30199     }
30200     
30201     
30202     
30203     
30204     
30205     
30206     
30207     
30208 });
30209
30210
30211 // back compat
30212 Roo.Form = Roo.form.Form;
30213 /*
30214  * Based on:
30215  * Ext JS Library 1.1.1
30216  * Copyright(c) 2006-2007, Ext JS, LLC.
30217  *
30218  * Originally Released Under LGPL - original licence link has changed is not relivant.
30219  *
30220  * Fork - LGPL
30221  * <script type="text/javascript">
30222  */
30223
30224 // as we use this in bootstrap.
30225 Roo.namespace('Roo.form');
30226  /**
30227  * @class Roo.form.Action
30228  * Internal Class used to handle form actions
30229  * @constructor
30230  * @param {Roo.form.BasicForm} el The form element or its id
30231  * @param {Object} config Configuration options
30232  */
30233
30234  
30235  
30236 // define the action interface
30237 Roo.form.Action = function(form, options){
30238     this.form = form;
30239     this.options = options || {};
30240 };
30241 /**
30242  * Client Validation Failed
30243  * @const 
30244  */
30245 Roo.form.Action.CLIENT_INVALID = 'client';
30246 /**
30247  * Server Validation Failed
30248  * @const 
30249  */
30250 Roo.form.Action.SERVER_INVALID = 'server';
30251  /**
30252  * Connect to Server Failed
30253  * @const 
30254  */
30255 Roo.form.Action.CONNECT_FAILURE = 'connect';
30256 /**
30257  * Reading Data from Server Failed
30258  * @const 
30259  */
30260 Roo.form.Action.LOAD_FAILURE = 'load';
30261
30262 Roo.form.Action.prototype = {
30263     type : 'default',
30264     failureType : undefined,
30265     response : undefined,
30266     result : undefined,
30267
30268     // interface method
30269     run : function(options){
30270
30271     },
30272
30273     // interface method
30274     success : function(response){
30275
30276     },
30277
30278     // interface method
30279     handleResponse : function(response){
30280
30281     },
30282
30283     // default connection failure
30284     failure : function(response){
30285         
30286         this.response = response;
30287         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30288         this.form.afterAction(this, false);
30289     },
30290
30291     processResponse : function(response){
30292         this.response = response;
30293         if(!response.responseText){
30294             return true;
30295         }
30296         this.result = this.handleResponse(response);
30297         return this.result;
30298     },
30299
30300     // utility functions used internally
30301     getUrl : function(appendParams){
30302         var url = this.options.url || this.form.url || this.form.el.dom.action;
30303         if(appendParams){
30304             var p = this.getParams();
30305             if(p){
30306                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30307             }
30308         }
30309         return url;
30310     },
30311
30312     getMethod : function(){
30313         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30314     },
30315
30316     getParams : function(){
30317         var bp = this.form.baseParams;
30318         var p = this.options.params;
30319         if(p){
30320             if(typeof p == "object"){
30321                 p = Roo.urlEncode(Roo.applyIf(p, bp));
30322             }else if(typeof p == 'string' && bp){
30323                 p += '&' + Roo.urlEncode(bp);
30324             }
30325         }else if(bp){
30326             p = Roo.urlEncode(bp);
30327         }
30328         return p;
30329     },
30330
30331     createCallback : function(){
30332         return {
30333             success: this.success,
30334             failure: this.failure,
30335             scope: this,
30336             timeout: (this.form.timeout*1000),
30337             upload: this.form.fileUpload ? this.success : undefined
30338         };
30339     }
30340 };
30341
30342 Roo.form.Action.Submit = function(form, options){
30343     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30344 };
30345
30346 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30347     type : 'submit',
30348
30349     haveProgress : false,
30350     uploadComplete : false,
30351     
30352     // uploadProgress indicator.
30353     uploadProgress : function()
30354     {
30355         if (!this.form.progressUrl) {
30356             return;
30357         }
30358         
30359         if (!this.haveProgress) {
30360             Roo.MessageBox.progress("Uploading", "Uploading");
30361         }
30362         if (this.uploadComplete) {
30363            Roo.MessageBox.hide();
30364            return;
30365         }
30366         
30367         this.haveProgress = true;
30368    
30369         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30370         
30371         var c = new Roo.data.Connection();
30372         c.request({
30373             url : this.form.progressUrl,
30374             params: {
30375                 id : uid
30376             },
30377             method: 'GET',
30378             success : function(req){
30379                //console.log(data);
30380                 var rdata = false;
30381                 var edata;
30382                 try  {
30383                    rdata = Roo.decode(req.responseText)
30384                 } catch (e) {
30385                     Roo.log("Invalid data from server..");
30386                     Roo.log(edata);
30387                     return;
30388                 }
30389                 if (!rdata || !rdata.success) {
30390                     Roo.log(rdata);
30391                     Roo.MessageBox.alert(Roo.encode(rdata));
30392                     return;
30393                 }
30394                 var data = rdata.data;
30395                 
30396                 if (this.uploadComplete) {
30397                    Roo.MessageBox.hide();
30398                    return;
30399                 }
30400                    
30401                 if (data){
30402                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30403                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30404                     );
30405                 }
30406                 this.uploadProgress.defer(2000,this);
30407             },
30408        
30409             failure: function(data) {
30410                 Roo.log('progress url failed ');
30411                 Roo.log(data);
30412             },
30413             scope : this
30414         });
30415            
30416     },
30417     
30418     
30419     run : function()
30420     {
30421         // run get Values on the form, so it syncs any secondary forms.
30422         this.form.getValues();
30423         
30424         var o = this.options;
30425         var method = this.getMethod();
30426         var isPost = method == 'POST';
30427         if(o.clientValidation === false || this.form.isValid()){
30428             
30429             if (this.form.progressUrl) {
30430                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30431                     (new Date() * 1) + '' + Math.random());
30432                     
30433             } 
30434             
30435             
30436             Roo.Ajax.request(Roo.apply(this.createCallback(), {
30437                 form:this.form.el.dom,
30438                 url:this.getUrl(!isPost),
30439                 method: method,
30440                 params:isPost ? this.getParams() : null,
30441                 isUpload: this.form.fileUpload,
30442                 formData : this.form.formData
30443             }));
30444             
30445             this.uploadProgress();
30446
30447         }else if (o.clientValidation !== false){ // client validation failed
30448             this.failureType = Roo.form.Action.CLIENT_INVALID;
30449             this.form.afterAction(this, false);
30450         }
30451     },
30452
30453     success : function(response)
30454     {
30455         this.uploadComplete= true;
30456         if (this.haveProgress) {
30457             Roo.MessageBox.hide();
30458         }
30459         
30460         
30461         var result = this.processResponse(response);
30462         if(result === true || result.success){
30463             this.form.afterAction(this, true);
30464             return;
30465         }
30466         if(result.errors){
30467             this.form.markInvalid(result.errors);
30468             this.failureType = Roo.form.Action.SERVER_INVALID;
30469         }
30470         this.form.afterAction(this, false);
30471     },
30472     failure : function(response)
30473     {
30474         this.uploadComplete= true;
30475         if (this.haveProgress) {
30476             Roo.MessageBox.hide();
30477         }
30478         
30479         this.response = response;
30480         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30481         this.form.afterAction(this, false);
30482     },
30483     
30484     handleResponse : function(response){
30485         if(this.form.errorReader){
30486             var rs = this.form.errorReader.read(response);
30487             var errors = [];
30488             if(rs.records){
30489                 for(var i = 0, len = rs.records.length; i < len; i++) {
30490                     var r = rs.records[i];
30491                     errors[i] = r.data;
30492                 }
30493             }
30494             if(errors.length < 1){
30495                 errors = null;
30496             }
30497             return {
30498                 success : rs.success,
30499                 errors : errors
30500             };
30501         }
30502         var ret = false;
30503         try {
30504             ret = Roo.decode(response.responseText);
30505         } catch (e) {
30506             ret = {
30507                 success: false,
30508                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30509                 errors : []
30510             };
30511         }
30512         return ret;
30513         
30514     }
30515 });
30516
30517
30518 Roo.form.Action.Load = function(form, options){
30519     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30520     this.reader = this.form.reader;
30521 };
30522
30523 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30524     type : 'load',
30525
30526     run : function(){
30527         
30528         Roo.Ajax.request(Roo.apply(
30529                 this.createCallback(), {
30530                     method:this.getMethod(),
30531                     url:this.getUrl(false),
30532                     params:this.getParams()
30533         }));
30534     },
30535
30536     success : function(response){
30537         
30538         var result = this.processResponse(response);
30539         if(result === true || !result.success || !result.data){
30540             this.failureType = Roo.form.Action.LOAD_FAILURE;
30541             this.form.afterAction(this, false);
30542             return;
30543         }
30544         this.form.clearInvalid();
30545         this.form.setValues(result.data);
30546         this.form.afterAction(this, true);
30547     },
30548
30549     handleResponse : function(response){
30550         if(this.form.reader){
30551             var rs = this.form.reader.read(response);
30552             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30553             return {
30554                 success : rs.success,
30555                 data : data
30556             };
30557         }
30558         return Roo.decode(response.responseText);
30559     }
30560 });
30561
30562 Roo.form.Action.ACTION_TYPES = {
30563     'load' : Roo.form.Action.Load,
30564     'submit' : Roo.form.Action.Submit
30565 };/*
30566  * Based on:
30567  * Ext JS Library 1.1.1
30568  * Copyright(c) 2006-2007, Ext JS, LLC.
30569  *
30570  * Originally Released Under LGPL - original licence link has changed is not relivant.
30571  *
30572  * Fork - LGPL
30573  * <script type="text/javascript">
30574  */
30575  
30576 /**
30577  * @class Roo.form.Layout
30578  * @extends Roo.Component
30579  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30580  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30581  * @constructor
30582  * @param {Object} config Configuration options
30583  */
30584 Roo.form.Layout = function(config){
30585     var xitems = [];
30586     if (config.items) {
30587         xitems = config.items;
30588         delete config.items;
30589     }
30590     Roo.form.Layout.superclass.constructor.call(this, config);
30591     this.stack = [];
30592     Roo.each(xitems, this.addxtype, this);
30593      
30594 };
30595
30596 Roo.extend(Roo.form.Layout, Roo.Component, {
30597     /**
30598      * @cfg {String/Object} autoCreate
30599      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30600      */
30601     /**
30602      * @cfg {String/Object/Function} style
30603      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30604      * a function which returns such a specification.
30605      */
30606     /**
30607      * @cfg {String} labelAlign
30608      * Valid values are "left," "top" and "right" (defaults to "left")
30609      */
30610     /**
30611      * @cfg {Number} labelWidth
30612      * Fixed width in pixels of all field labels (defaults to undefined)
30613      */
30614     /**
30615      * @cfg {Boolean} clear
30616      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30617      */
30618     clear : true,
30619     /**
30620      * @cfg {String} labelSeparator
30621      * The separator to use after field labels (defaults to ':')
30622      */
30623     labelSeparator : ':',
30624     /**
30625      * @cfg {Boolean} hideLabels
30626      * True to suppress the display of field labels in this layout (defaults to false)
30627      */
30628     hideLabels : false,
30629
30630     // private
30631     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30632     
30633     isLayout : true,
30634     
30635     // private
30636     onRender : function(ct, position){
30637         if(this.el){ // from markup
30638             this.el = Roo.get(this.el);
30639         }else {  // generate
30640             var cfg = this.getAutoCreate();
30641             this.el = ct.createChild(cfg, position);
30642         }
30643         if(this.style){
30644             this.el.applyStyles(this.style);
30645         }
30646         if(this.labelAlign){
30647             this.el.addClass('x-form-label-'+this.labelAlign);
30648         }
30649         if(this.hideLabels){
30650             this.labelStyle = "display:none";
30651             this.elementStyle = "padding-left:0;";
30652         }else{
30653             if(typeof this.labelWidth == 'number'){
30654                 this.labelStyle = "width:"+this.labelWidth+"px;";
30655                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30656             }
30657             if(this.labelAlign == 'top'){
30658                 this.labelStyle = "width:auto;";
30659                 this.elementStyle = "padding-left:0;";
30660             }
30661         }
30662         var stack = this.stack;
30663         var slen = stack.length;
30664         if(slen > 0){
30665             if(!this.fieldTpl){
30666                 var t = new Roo.Template(
30667                     '<div class="x-form-item {5}">',
30668                         '<label for="{0}" style="{2}">{1}{4}</label>',
30669                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30670                         '</div>',
30671                     '</div><div class="x-form-clear-left"></div>'
30672                 );
30673                 t.disableFormats = true;
30674                 t.compile();
30675                 Roo.form.Layout.prototype.fieldTpl = t;
30676             }
30677             for(var i = 0; i < slen; i++) {
30678                 if(stack[i].isFormField){
30679                     this.renderField(stack[i]);
30680                 }else{
30681                     this.renderComponent(stack[i]);
30682                 }
30683             }
30684         }
30685         if(this.clear){
30686             this.el.createChild({cls:'x-form-clear'});
30687         }
30688     },
30689
30690     // private
30691     renderField : function(f){
30692         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30693                f.id, //0
30694                f.fieldLabel, //1
30695                f.labelStyle||this.labelStyle||'', //2
30696                this.elementStyle||'', //3
30697                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30698                f.itemCls||this.itemCls||''  //5
30699        ], true).getPrevSibling());
30700     },
30701
30702     // private
30703     renderComponent : function(c){
30704         c.render(c.isLayout ? this.el : this.el.createChild());    
30705     },
30706     /**
30707      * Adds a object form elements (using the xtype property as the factory method.)
30708      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30709      * @param {Object} config 
30710      */
30711     addxtype : function(o)
30712     {
30713         // create the lement.
30714         o.form = this.form;
30715         var fe = Roo.factory(o, Roo.form);
30716         this.form.allItems.push(fe);
30717         this.stack.push(fe);
30718         
30719         if (fe.isFormField) {
30720             this.form.items.add(fe);
30721         }
30722          
30723         return fe;
30724     }
30725 });
30726
30727 /**
30728  * @class Roo.form.Column
30729  * @extends Roo.form.Layout
30730  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30731  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30732  * @constructor
30733  * @param {Object} config Configuration options
30734  */
30735 Roo.form.Column = function(config){
30736     Roo.form.Column.superclass.constructor.call(this, config);
30737 };
30738
30739 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30740     /**
30741      * @cfg {Number/String} width
30742      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30743      */
30744     /**
30745      * @cfg {String/Object} autoCreate
30746      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30747      */
30748
30749     // private
30750     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30751
30752     // private
30753     onRender : function(ct, position){
30754         Roo.form.Column.superclass.onRender.call(this, ct, position);
30755         if(this.width){
30756             this.el.setWidth(this.width);
30757         }
30758     }
30759 });
30760
30761
30762 /**
30763  * @class Roo.form.Row
30764  * @extends Roo.form.Layout
30765  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30766  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30767  * @constructor
30768  * @param {Object} config Configuration options
30769  */
30770
30771  
30772 Roo.form.Row = function(config){
30773     Roo.form.Row.superclass.constructor.call(this, config);
30774 };
30775  
30776 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30777       /**
30778      * @cfg {Number/String} width
30779      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30780      */
30781     /**
30782      * @cfg {Number/String} height
30783      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30784      */
30785     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30786     
30787     padWidth : 20,
30788     // private
30789     onRender : function(ct, position){
30790         //console.log('row render');
30791         if(!this.rowTpl){
30792             var t = new Roo.Template(
30793                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30794                     '<label for="{0}" style="{2}">{1}{4}</label>',
30795                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30796                     '</div>',
30797                 '</div>'
30798             );
30799             t.disableFormats = true;
30800             t.compile();
30801             Roo.form.Layout.prototype.rowTpl = t;
30802         }
30803         this.fieldTpl = this.rowTpl;
30804         
30805         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30806         var labelWidth = 100;
30807         
30808         if ((this.labelAlign != 'top')) {
30809             if (typeof this.labelWidth == 'number') {
30810                 labelWidth = this.labelWidth
30811             }
30812             this.padWidth =  20 + labelWidth;
30813             
30814         }
30815         
30816         Roo.form.Column.superclass.onRender.call(this, ct, position);
30817         if(this.width){
30818             this.el.setWidth(this.width);
30819         }
30820         if(this.height){
30821             this.el.setHeight(this.height);
30822         }
30823     },
30824     
30825     // private
30826     renderField : function(f){
30827         f.fieldEl = this.fieldTpl.append(this.el, [
30828                f.id, f.fieldLabel,
30829                f.labelStyle||this.labelStyle||'',
30830                this.elementStyle||'',
30831                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30832                f.itemCls||this.itemCls||'',
30833                f.width ? f.width + this.padWidth : 160 + this.padWidth
30834        ],true);
30835     }
30836 });
30837  
30838
30839 /**
30840  * @class Roo.form.FieldSet
30841  * @extends Roo.form.Layout
30842  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
30843  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30844  * @constructor
30845  * @param {Object} config Configuration options
30846  */
30847 Roo.form.FieldSet = function(config){
30848     Roo.form.FieldSet.superclass.constructor.call(this, config);
30849 };
30850
30851 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30852     /**
30853      * @cfg {String} legend
30854      * The text to display as the legend for the FieldSet (defaults to '')
30855      */
30856     /**
30857      * @cfg {String/Object} autoCreate
30858      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30859      */
30860
30861     // private
30862     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30863
30864     // private
30865     onRender : function(ct, position){
30866         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30867         if(this.legend){
30868             this.setLegend(this.legend);
30869         }
30870     },
30871
30872     // private
30873     setLegend : function(text){
30874         if(this.rendered){
30875             this.el.child('legend').update(text);
30876         }
30877     }
30878 });/*
30879  * Based on:
30880  * Ext JS Library 1.1.1
30881  * Copyright(c) 2006-2007, Ext JS, LLC.
30882  *
30883  * Originally Released Under LGPL - original licence link has changed is not relivant.
30884  *
30885  * Fork - LGPL
30886  * <script type="text/javascript">
30887  */
30888 /**
30889  * @class Roo.form.VTypes
30890  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30891  * @static
30892  */
30893 Roo.form.VTypes = function(){
30894     // closure these in so they are only created once.
30895     var alpha = /^[a-zA-Z_]+$/;
30896     var alphanum = /^[a-zA-Z0-9_]+$/;
30897     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30898     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30899
30900     // All these messages and functions are configurable
30901     return {
30902         /**
30903          * The function used to validate email addresses
30904          * @param {String} value The email address
30905          */
30906         'email' : function(v){
30907             return email.test(v);
30908         },
30909         /**
30910          * The error text to display when the email validation function returns false
30911          * @type String
30912          */
30913         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30914         /**
30915          * The keystroke filter mask to be applied on email input
30916          * @type RegExp
30917          */
30918         'emailMask' : /[a-z0-9_\.\-@]/i,
30919
30920         /**
30921          * The function used to validate URLs
30922          * @param {String} value The URL
30923          */
30924         'url' : function(v){
30925             return url.test(v);
30926         },
30927         /**
30928          * The error text to display when the url validation function returns false
30929          * @type String
30930          */
30931         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30932         
30933         /**
30934          * The function used to validate alpha values
30935          * @param {String} value The value
30936          */
30937         'alpha' : function(v){
30938             return alpha.test(v);
30939         },
30940         /**
30941          * The error text to display when the alpha validation function returns false
30942          * @type String
30943          */
30944         'alphaText' : 'This field should only contain letters and _',
30945         /**
30946          * The keystroke filter mask to be applied on alpha input
30947          * @type RegExp
30948          */
30949         'alphaMask' : /[a-z_]/i,
30950
30951         /**
30952          * The function used to validate alphanumeric values
30953          * @param {String} value The value
30954          */
30955         'alphanum' : function(v){
30956             return alphanum.test(v);
30957         },
30958         /**
30959          * The error text to display when the alphanumeric validation function returns false
30960          * @type String
30961          */
30962         'alphanumText' : 'This field should only contain letters, numbers and _',
30963         /**
30964          * The keystroke filter mask to be applied on alphanumeric input
30965          * @type RegExp
30966          */
30967         'alphanumMask' : /[a-z0-9_]/i
30968     };
30969 }();//<script type="text/javascript">
30970
30971 /**
30972  * @class Roo.form.FCKeditor
30973  * @extends Roo.form.TextArea
30974  * Wrapper around the FCKEditor http://www.fckeditor.net
30975  * @constructor
30976  * Creates a new FCKeditor
30977  * @param {Object} config Configuration options
30978  */
30979 Roo.form.FCKeditor = function(config){
30980     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30981     this.addEvents({
30982          /**
30983          * @event editorinit
30984          * Fired when the editor is initialized - you can add extra handlers here..
30985          * @param {FCKeditor} this
30986          * @param {Object} the FCK object.
30987          */
30988         editorinit : true
30989     });
30990     
30991     
30992 };
30993 Roo.form.FCKeditor.editors = { };
30994 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30995 {
30996     //defaultAutoCreate : {
30997     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30998     //},
30999     // private
31000     /**
31001      * @cfg {Object} fck options - see fck manual for details.
31002      */
31003     fckconfig : false,
31004     
31005     /**
31006      * @cfg {Object} fck toolbar set (Basic or Default)
31007      */
31008     toolbarSet : 'Basic',
31009     /**
31010      * @cfg {Object} fck BasePath
31011      */ 
31012     basePath : '/fckeditor/',
31013     
31014     
31015     frame : false,
31016     
31017     value : '',
31018     
31019    
31020     onRender : function(ct, position)
31021     {
31022         if(!this.el){
31023             this.defaultAutoCreate = {
31024                 tag: "textarea",
31025                 style:"width:300px;height:60px;",
31026                 autocomplete: "new-password"
31027             };
31028         }
31029         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
31030         /*
31031         if(this.grow){
31032             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
31033             if(this.preventScrollbars){
31034                 this.el.setStyle("overflow", "hidden");
31035             }
31036             this.el.setHeight(this.growMin);
31037         }
31038         */
31039         //console.log('onrender' + this.getId() );
31040         Roo.form.FCKeditor.editors[this.getId()] = this;
31041          
31042
31043         this.replaceTextarea() ;
31044         
31045     },
31046     
31047     getEditor : function() {
31048         return this.fckEditor;
31049     },
31050     /**
31051      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
31052      * @param {Mixed} value The value to set
31053      */
31054     
31055     
31056     setValue : function(value)
31057     {
31058         //console.log('setValue: ' + value);
31059         
31060         if(typeof(value) == 'undefined') { // not sure why this is happending...
31061             return;
31062         }
31063         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31064         
31065         //if(!this.el || !this.getEditor()) {
31066         //    this.value = value;
31067             //this.setValue.defer(100,this,[value]);    
31068         //    return;
31069         //} 
31070         
31071         if(!this.getEditor()) {
31072             return;
31073         }
31074         
31075         this.getEditor().SetData(value);
31076         
31077         //
31078
31079     },
31080
31081     /**
31082      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
31083      * @return {Mixed} value The field value
31084      */
31085     getValue : function()
31086     {
31087         
31088         if (this.frame && this.frame.dom.style.display == 'none') {
31089             return Roo.form.FCKeditor.superclass.getValue.call(this);
31090         }
31091         
31092         if(!this.el || !this.getEditor()) {
31093            
31094            // this.getValue.defer(100,this); 
31095             return this.value;
31096         }
31097        
31098         
31099         var value=this.getEditor().GetData();
31100         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31101         return Roo.form.FCKeditor.superclass.getValue.call(this);
31102         
31103
31104     },
31105
31106     /**
31107      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
31108      * @return {Mixed} value The field value
31109      */
31110     getRawValue : function()
31111     {
31112         if (this.frame && this.frame.dom.style.display == 'none') {
31113             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31114         }
31115         
31116         if(!this.el || !this.getEditor()) {
31117             //this.getRawValue.defer(100,this); 
31118             return this.value;
31119             return;
31120         }
31121         
31122         
31123         
31124         var value=this.getEditor().GetData();
31125         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31126         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31127          
31128     },
31129     
31130     setSize : function(w,h) {
31131         
31132         
31133         
31134         //if (this.frame && this.frame.dom.style.display == 'none') {
31135         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31136         //    return;
31137         //}
31138         //if(!this.el || !this.getEditor()) {
31139         //    this.setSize.defer(100,this, [w,h]); 
31140         //    return;
31141         //}
31142         
31143         
31144         
31145         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31146         
31147         this.frame.dom.setAttribute('width', w);
31148         this.frame.dom.setAttribute('height', h);
31149         this.frame.setSize(w,h);
31150         
31151     },
31152     
31153     toggleSourceEdit : function(value) {
31154         
31155       
31156          
31157         this.el.dom.style.display = value ? '' : 'none';
31158         this.frame.dom.style.display = value ?  'none' : '';
31159         
31160     },
31161     
31162     
31163     focus: function(tag)
31164     {
31165         if (this.frame.dom.style.display == 'none') {
31166             return Roo.form.FCKeditor.superclass.focus.call(this);
31167         }
31168         if(!this.el || !this.getEditor()) {
31169             this.focus.defer(100,this, [tag]); 
31170             return;
31171         }
31172         
31173         
31174         
31175         
31176         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31177         this.getEditor().Focus();
31178         if (tgs.length) {
31179             if (!this.getEditor().Selection.GetSelection()) {
31180                 this.focus.defer(100,this, [tag]); 
31181                 return;
31182             }
31183             
31184             
31185             var r = this.getEditor().EditorDocument.createRange();
31186             r.setStart(tgs[0],0);
31187             r.setEnd(tgs[0],0);
31188             this.getEditor().Selection.GetSelection().removeAllRanges();
31189             this.getEditor().Selection.GetSelection().addRange(r);
31190             this.getEditor().Focus();
31191         }
31192         
31193     },
31194     
31195     
31196     
31197     replaceTextarea : function()
31198     {
31199         if ( document.getElementById( this.getId() + '___Frame' ) ) {
31200             return ;
31201         }
31202         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31203         //{
31204             // We must check the elements firstly using the Id and then the name.
31205         var oTextarea = document.getElementById( this.getId() );
31206         
31207         var colElementsByName = document.getElementsByName( this.getId() ) ;
31208          
31209         oTextarea.style.display = 'none' ;
31210
31211         if ( oTextarea.tabIndex ) {            
31212             this.TabIndex = oTextarea.tabIndex ;
31213         }
31214         
31215         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31216         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31217         this.frame = Roo.get(this.getId() + '___Frame')
31218     },
31219     
31220     _getConfigHtml : function()
31221     {
31222         var sConfig = '' ;
31223
31224         for ( var o in this.fckconfig ) {
31225             sConfig += sConfig.length > 0  ? '&amp;' : '';
31226             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31227         }
31228
31229         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31230     },
31231     
31232     
31233     _getIFrameHtml : function()
31234     {
31235         var sFile = 'fckeditor.html' ;
31236         /* no idea what this is about..
31237         try
31238         {
31239             if ( (/fcksource=true/i).test( window.top.location.search ) )
31240                 sFile = 'fckeditor.original.html' ;
31241         }
31242         catch (e) { 
31243         */
31244
31245         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31246         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
31247         
31248         
31249         var html = '<iframe id="' + this.getId() +
31250             '___Frame" src="' + sLink +
31251             '" width="' + this.width +
31252             '" height="' + this.height + '"' +
31253             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
31254             ' frameborder="0" scrolling="no"></iframe>' ;
31255
31256         return html ;
31257     },
31258     
31259     _insertHtmlBefore : function( html, element )
31260     {
31261         if ( element.insertAdjacentHTML )       {
31262             // IE
31263             element.insertAdjacentHTML( 'beforeBegin', html ) ;
31264         } else { // Gecko
31265             var oRange = document.createRange() ;
31266             oRange.setStartBefore( element ) ;
31267             var oFragment = oRange.createContextualFragment( html );
31268             element.parentNode.insertBefore( oFragment, element ) ;
31269         }
31270     }
31271     
31272     
31273   
31274     
31275     
31276     
31277     
31278
31279 });
31280
31281 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31282
31283 function FCKeditor_OnComplete(editorInstance){
31284     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31285     f.fckEditor = editorInstance;
31286     //console.log("loaded");
31287     f.fireEvent('editorinit', f, editorInstance);
31288
31289   
31290
31291  
31292
31293
31294
31295
31296
31297
31298
31299
31300
31301
31302
31303
31304
31305
31306
31307 //<script type="text/javascript">
31308 /**
31309  * @class Roo.form.GridField
31310  * @extends Roo.form.Field
31311  * Embed a grid (or editable grid into a form)
31312  * STATUS ALPHA
31313  * 
31314  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31315  * it needs 
31316  * xgrid.store = Roo.data.Store
31317  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31318  * xgrid.store.reader = Roo.data.JsonReader 
31319  * 
31320  * 
31321  * @constructor
31322  * Creates a new GridField
31323  * @param {Object} config Configuration options
31324  */
31325 Roo.form.GridField = function(config){
31326     Roo.form.GridField.superclass.constructor.call(this, config);
31327      
31328 };
31329
31330 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
31331     /**
31332      * @cfg {Number} width  - used to restrict width of grid..
31333      */
31334     width : 100,
31335     /**
31336      * @cfg {Number} height - used to restrict height of grid..
31337      */
31338     height : 50,
31339      /**
31340      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31341          * 
31342          *}
31343      */
31344     xgrid : false, 
31345     /**
31346      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31347      * {tag: "input", type: "checkbox", autocomplete: "off"})
31348      */
31349    // defaultAutoCreate : { tag: 'div' },
31350     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31351     /**
31352      * @cfg {String} addTitle Text to include for adding a title.
31353      */
31354     addTitle : false,
31355     //
31356     onResize : function(){
31357         Roo.form.Field.superclass.onResize.apply(this, arguments);
31358     },
31359
31360     initEvents : function(){
31361         // Roo.form.Checkbox.superclass.initEvents.call(this);
31362         // has no events...
31363        
31364     },
31365
31366
31367     getResizeEl : function(){
31368         return this.wrap;
31369     },
31370
31371     getPositionEl : function(){
31372         return this.wrap;
31373     },
31374
31375     // private
31376     onRender : function(ct, position){
31377         
31378         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31379         var style = this.style;
31380         delete this.style;
31381         
31382         Roo.form.GridField.superclass.onRender.call(this, ct, position);
31383         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31384         this.viewEl = this.wrap.createChild({ tag: 'div' });
31385         if (style) {
31386             this.viewEl.applyStyles(style);
31387         }
31388         if (this.width) {
31389             this.viewEl.setWidth(this.width);
31390         }
31391         if (this.height) {
31392             this.viewEl.setHeight(this.height);
31393         }
31394         //if(this.inputValue !== undefined){
31395         //this.setValue(this.value);
31396         
31397         
31398         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31399         
31400         
31401         this.grid.render();
31402         this.grid.getDataSource().on('remove', this.refreshValue, this);
31403         this.grid.getDataSource().on('update', this.refreshValue, this);
31404         this.grid.on('afteredit', this.refreshValue, this);
31405  
31406     },
31407      
31408     
31409     /**
31410      * Sets the value of the item. 
31411      * @param {String} either an object  or a string..
31412      */
31413     setValue : function(v){
31414         //this.value = v;
31415         v = v || []; // empty set..
31416         // this does not seem smart - it really only affects memoryproxy grids..
31417         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31418             var ds = this.grid.getDataSource();
31419             // assumes a json reader..
31420             var data = {}
31421             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
31422             ds.loadData( data);
31423         }
31424         // clear selection so it does not get stale.
31425         if (this.grid.sm) { 
31426             this.grid.sm.clearSelections();
31427         }
31428         
31429         Roo.form.GridField.superclass.setValue.call(this, v);
31430         this.refreshValue();
31431         // should load data in the grid really....
31432     },
31433     
31434     // private
31435     refreshValue: function() {
31436          var val = [];
31437         this.grid.getDataSource().each(function(r) {
31438             val.push(r.data);
31439         });
31440         this.el.dom.value = Roo.encode(val);
31441     }
31442     
31443      
31444     
31445     
31446 });/*
31447  * Based on:
31448  * Ext JS Library 1.1.1
31449  * Copyright(c) 2006-2007, Ext JS, LLC.
31450  *
31451  * Originally Released Under LGPL - original licence link has changed is not relivant.
31452  *
31453  * Fork - LGPL
31454  * <script type="text/javascript">
31455  */
31456 /**
31457  * @class Roo.form.DisplayField
31458  * @extends Roo.form.Field
31459  * A generic Field to display non-editable data.
31460  * @cfg {Boolean} closable (true|false) default false
31461  * @constructor
31462  * Creates a new Display Field item.
31463  * @param {Object} config Configuration options
31464  */
31465 Roo.form.DisplayField = function(config){
31466     Roo.form.DisplayField.superclass.constructor.call(this, config);
31467     
31468     this.addEvents({
31469         /**
31470          * @event close
31471          * Fires after the click the close btn
31472              * @param {Roo.form.DisplayField} this
31473              */
31474         close : true
31475     });
31476 };
31477
31478 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
31479     inputType:      'hidden',
31480     allowBlank:     true,
31481     readOnly:         true,
31482     
31483  
31484     /**
31485      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31486      */
31487     focusClass : undefined,
31488     /**
31489      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31490      */
31491     fieldClass: 'x-form-field',
31492     
31493      /**
31494      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31495      */
31496     valueRenderer: undefined,
31497     
31498     width: 100,
31499     /**
31500      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31501      * {tag: "input", type: "checkbox", autocomplete: "off"})
31502      */
31503      
31504  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31505  
31506     closable : false,
31507     
31508     onResize : function(){
31509         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31510         
31511     },
31512
31513     initEvents : function(){
31514         // Roo.form.Checkbox.superclass.initEvents.call(this);
31515         // has no events...
31516         
31517         if(this.closable){
31518             this.closeEl.on('click', this.onClose, this);
31519         }
31520        
31521     },
31522
31523
31524     getResizeEl : function(){
31525         return this.wrap;
31526     },
31527
31528     getPositionEl : function(){
31529         return this.wrap;
31530     },
31531
31532     // private
31533     onRender : function(ct, position){
31534         
31535         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31536         //if(this.inputValue !== undefined){
31537         this.wrap = this.el.wrap();
31538         
31539         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31540         
31541         if(this.closable){
31542             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31543         }
31544         
31545         if (this.bodyStyle) {
31546             this.viewEl.applyStyles(this.bodyStyle);
31547         }
31548         //this.viewEl.setStyle('padding', '2px');
31549         
31550         this.setValue(this.value);
31551         
31552     },
31553 /*
31554     // private
31555     initValue : Roo.emptyFn,
31556
31557   */
31558
31559         // private
31560     onClick : function(){
31561         
31562     },
31563
31564     /**
31565      * Sets the checked state of the checkbox.
31566      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31567      */
31568     setValue : function(v){
31569         this.value = v;
31570         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31571         // this might be called before we have a dom element..
31572         if (!this.viewEl) {
31573             return;
31574         }
31575         this.viewEl.dom.innerHTML = html;
31576         Roo.form.DisplayField.superclass.setValue.call(this, v);
31577
31578     },
31579     
31580     onClose : function(e)
31581     {
31582         e.preventDefault();
31583         
31584         this.fireEvent('close', this);
31585     }
31586 });/*
31587  * 
31588  * Licence- LGPL
31589  * 
31590  */
31591
31592 /**
31593  * @class Roo.form.DayPicker
31594  * @extends Roo.form.Field
31595  * A Day picker show [M] [T] [W] ....
31596  * @constructor
31597  * Creates a new Day Picker
31598  * @param {Object} config Configuration options
31599  */
31600 Roo.form.DayPicker= function(config){
31601     Roo.form.DayPicker.superclass.constructor.call(this, config);
31602      
31603 };
31604
31605 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31606     /**
31607      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31608      */
31609     focusClass : undefined,
31610     /**
31611      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31612      */
31613     fieldClass: "x-form-field",
31614    
31615     /**
31616      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31617      * {tag: "input", type: "checkbox", autocomplete: "off"})
31618      */
31619     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31620     
31621    
31622     actionMode : 'viewEl', 
31623     //
31624     // private
31625  
31626     inputType : 'hidden',
31627     
31628      
31629     inputElement: false, // real input element?
31630     basedOn: false, // ????
31631     
31632     isFormField: true, // not sure where this is needed!!!!
31633
31634     onResize : function(){
31635         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31636         if(!this.boxLabel){
31637             this.el.alignTo(this.wrap, 'c-c');
31638         }
31639     },
31640
31641     initEvents : function(){
31642         Roo.form.Checkbox.superclass.initEvents.call(this);
31643         this.el.on("click", this.onClick,  this);
31644         this.el.on("change", this.onClick,  this);
31645     },
31646
31647
31648     getResizeEl : function(){
31649         return this.wrap;
31650     },
31651
31652     getPositionEl : function(){
31653         return this.wrap;
31654     },
31655
31656     
31657     // private
31658     onRender : function(ct, position){
31659         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31660        
31661         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31662         
31663         var r1 = '<table><tr>';
31664         var r2 = '<tr class="x-form-daypick-icons">';
31665         for (var i=0; i < 7; i++) {
31666             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31667             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31668         }
31669         
31670         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31671         viewEl.select('img').on('click', this.onClick, this);
31672         this.viewEl = viewEl;   
31673         
31674         
31675         // this will not work on Chrome!!!
31676         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31677         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31678         
31679         
31680           
31681
31682     },
31683
31684     // private
31685     initValue : Roo.emptyFn,
31686
31687     /**
31688      * Returns the checked state of the checkbox.
31689      * @return {Boolean} True if checked, else false
31690      */
31691     getValue : function(){
31692         return this.el.dom.value;
31693         
31694     },
31695
31696         // private
31697     onClick : function(e){ 
31698         //this.setChecked(!this.checked);
31699         Roo.get(e.target).toggleClass('x-menu-item-checked');
31700         this.refreshValue();
31701         //if(this.el.dom.checked != this.checked){
31702         //    this.setValue(this.el.dom.checked);
31703        // }
31704     },
31705     
31706     // private
31707     refreshValue : function()
31708     {
31709         var val = '';
31710         this.viewEl.select('img',true).each(function(e,i,n)  {
31711             val += e.is(".x-menu-item-checked") ? String(n) : '';
31712         });
31713         this.setValue(val, true);
31714     },
31715
31716     /**
31717      * Sets the checked state of the checkbox.
31718      * On is always based on a string comparison between inputValue and the param.
31719      * @param {Boolean/String} value - the value to set 
31720      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31721      */
31722     setValue : function(v,suppressEvent){
31723         if (!this.el.dom) {
31724             return;
31725         }
31726         var old = this.el.dom.value ;
31727         this.el.dom.value = v;
31728         if (suppressEvent) {
31729             return ;
31730         }
31731          
31732         // update display..
31733         this.viewEl.select('img',true).each(function(e,i,n)  {
31734             
31735             var on = e.is(".x-menu-item-checked");
31736             var newv = v.indexOf(String(n)) > -1;
31737             if (on != newv) {
31738                 e.toggleClass('x-menu-item-checked');
31739             }
31740             
31741         });
31742         
31743         
31744         this.fireEvent('change', this, v, old);
31745         
31746         
31747     },
31748    
31749     // handle setting of hidden value by some other method!!?!?
31750     setFromHidden: function()
31751     {
31752         if(!this.el){
31753             return;
31754         }
31755         //console.log("SET FROM HIDDEN");
31756         //alert('setFrom hidden');
31757         this.setValue(this.el.dom.value);
31758     },
31759     
31760     onDestroy : function()
31761     {
31762         if(this.viewEl){
31763             Roo.get(this.viewEl).remove();
31764         }
31765          
31766         Roo.form.DayPicker.superclass.onDestroy.call(this);
31767     }
31768
31769 });/*
31770  * RooJS Library 1.1.1
31771  * Copyright(c) 2008-2011  Alan Knowles
31772  *
31773  * License - LGPL
31774  */
31775  
31776
31777 /**
31778  * @class Roo.form.ComboCheck
31779  * @extends Roo.form.ComboBox
31780  * A combobox for multiple select items.
31781  *
31782  * FIXME - could do with a reset button..
31783  * 
31784  * @constructor
31785  * Create a new ComboCheck
31786  * @param {Object} config Configuration options
31787  */
31788 Roo.form.ComboCheck = function(config){
31789     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31790     // should verify some data...
31791     // like
31792     // hiddenName = required..
31793     // displayField = required
31794     // valudField == required
31795     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31796     var _t = this;
31797     Roo.each(req, function(e) {
31798         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31799             throw "Roo.form.ComboCheck : missing value for: " + e;
31800         }
31801     });
31802     
31803     
31804 };
31805
31806 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31807      
31808      
31809     editable : false,
31810      
31811     selectedClass: 'x-menu-item-checked', 
31812     
31813     // private
31814     onRender : function(ct, position){
31815         var _t = this;
31816         
31817         
31818         
31819         if(!this.tpl){
31820             var cls = 'x-combo-list';
31821
31822             
31823             this.tpl =  new Roo.Template({
31824                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31825                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31826                    '<span>{' + this.displayField + '}</span>' +
31827                     '</div>' 
31828                 
31829             });
31830         }
31831  
31832         
31833         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31834         this.view.singleSelect = false;
31835         this.view.multiSelect = true;
31836         this.view.toggleSelect = true;
31837         this.pageTb.add(new Roo.Toolbar.Fill(), {
31838             
31839             text: 'Done',
31840             handler: function()
31841             {
31842                 _t.collapse();
31843             }
31844         });
31845     },
31846     
31847     onViewOver : function(e, t){
31848         // do nothing...
31849         return;
31850         
31851     },
31852     
31853     onViewClick : function(doFocus,index){
31854         return;
31855         
31856     },
31857     select: function () {
31858         //Roo.log("SELECT CALLED");
31859     },
31860      
31861     selectByValue : function(xv, scrollIntoView){
31862         var ar = this.getValueArray();
31863         var sels = [];
31864         
31865         Roo.each(ar, function(v) {
31866             if(v === undefined || v === null){
31867                 return;
31868             }
31869             var r = this.findRecord(this.valueField, v);
31870             if(r){
31871                 sels.push(this.store.indexOf(r))
31872                 
31873             }
31874         },this);
31875         this.view.select(sels);
31876         return false;
31877     },
31878     
31879     
31880     
31881     onSelect : function(record, index){
31882        // Roo.log("onselect Called");
31883        // this is only called by the clear button now..
31884         this.view.clearSelections();
31885         this.setValue('[]');
31886         if (this.value != this.valueBefore) {
31887             this.fireEvent('change', this, this.value, this.valueBefore);
31888             this.valueBefore = this.value;
31889         }
31890     },
31891     getValueArray : function()
31892     {
31893         var ar = [] ;
31894         
31895         try {
31896             //Roo.log(this.value);
31897             if (typeof(this.value) == 'undefined') {
31898                 return [];
31899             }
31900             var ar = Roo.decode(this.value);
31901             return  ar instanceof Array ? ar : []; //?? valid?
31902             
31903         } catch(e) {
31904             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31905             return [];
31906         }
31907          
31908     },
31909     expand : function ()
31910     {
31911         
31912         Roo.form.ComboCheck.superclass.expand.call(this);
31913         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31914         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31915         
31916
31917     },
31918     
31919     collapse : function(){
31920         Roo.form.ComboCheck.superclass.collapse.call(this);
31921         var sl = this.view.getSelectedIndexes();
31922         var st = this.store;
31923         var nv = [];
31924         var tv = [];
31925         var r;
31926         Roo.each(sl, function(i) {
31927             r = st.getAt(i);
31928             nv.push(r.get(this.valueField));
31929         },this);
31930         this.setValue(Roo.encode(nv));
31931         if (this.value != this.valueBefore) {
31932
31933             this.fireEvent('change', this, this.value, this.valueBefore);
31934             this.valueBefore = this.value;
31935         }
31936         
31937     },
31938     
31939     setValue : function(v){
31940         // Roo.log(v);
31941         this.value = v;
31942         
31943         var vals = this.getValueArray();
31944         var tv = [];
31945         Roo.each(vals, function(k) {
31946             var r = this.findRecord(this.valueField, k);
31947             if(r){
31948                 tv.push(r.data[this.displayField]);
31949             }else if(this.valueNotFoundText !== undefined){
31950                 tv.push( this.valueNotFoundText );
31951             }
31952         },this);
31953        // Roo.log(tv);
31954         
31955         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31956         this.hiddenField.value = v;
31957         this.value = v;
31958     }
31959     
31960 });/*
31961  * Based on:
31962  * Ext JS Library 1.1.1
31963  * Copyright(c) 2006-2007, Ext JS, LLC.
31964  *
31965  * Originally Released Under LGPL - original licence link has changed is not relivant.
31966  *
31967  * Fork - LGPL
31968  * <script type="text/javascript">
31969  */
31970  
31971 /**
31972  * @class Roo.form.Signature
31973  * @extends Roo.form.Field
31974  * Signature field.  
31975  * @constructor
31976  * 
31977  * @param {Object} config Configuration options
31978  */
31979
31980 Roo.form.Signature = function(config){
31981     Roo.form.Signature.superclass.constructor.call(this, config);
31982     
31983     this.addEvents({// not in used??
31984          /**
31985          * @event confirm
31986          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31987              * @param {Roo.form.Signature} combo This combo box
31988              */
31989         'confirm' : true,
31990         /**
31991          * @event reset
31992          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31993              * @param {Roo.form.ComboBox} combo This combo box
31994              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31995              */
31996         'reset' : true
31997     });
31998 };
31999
32000 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
32001     /**
32002      * @cfg {Object} labels Label to use when rendering a form.
32003      * defaults to 
32004      * labels : { 
32005      *      clear : "Clear",
32006      *      confirm : "Confirm"
32007      *  }
32008      */
32009     labels : { 
32010         clear : "Clear",
32011         confirm : "Confirm"
32012     },
32013     /**
32014      * @cfg {Number} width The signature panel width (defaults to 300)
32015      */
32016     width: 300,
32017     /**
32018      * @cfg {Number} height The signature panel height (defaults to 100)
32019      */
32020     height : 100,
32021     /**
32022      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
32023      */
32024     allowBlank : false,
32025     
32026     //private
32027     // {Object} signPanel The signature SVG panel element (defaults to {})
32028     signPanel : {},
32029     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
32030     isMouseDown : false,
32031     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
32032     isConfirmed : false,
32033     // {String} signatureTmp SVG mapping string (defaults to empty string)
32034     signatureTmp : '',
32035     
32036     
32037     defaultAutoCreate : { // modified by initCompnoent..
32038         tag: "input",
32039         type:"hidden"
32040     },
32041
32042     // private
32043     onRender : function(ct, position){
32044         
32045         Roo.form.Signature.superclass.onRender.call(this, ct, position);
32046         
32047         this.wrap = this.el.wrap({
32048             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32049         });
32050         
32051         this.createToolbar(this);
32052         this.signPanel = this.wrap.createChild({
32053                 tag: 'div',
32054                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32055             }, this.el
32056         );
32057             
32058         this.svgID = Roo.id();
32059         this.svgEl = this.signPanel.createChild({
32060               xmlns : 'http://www.w3.org/2000/svg',
32061               tag : 'svg',
32062               id : this.svgID + "-svg",
32063               width: this.width,
32064               height: this.height,
32065               viewBox: '0 0 '+this.width+' '+this.height,
32066               cn : [
32067                 {
32068                     tag: "rect",
32069                     id: this.svgID + "-svg-r",
32070                     width: this.width,
32071                     height: this.height,
32072                     fill: "#ffa"
32073                 },
32074                 {
32075                     tag: "line",
32076                     id: this.svgID + "-svg-l",
32077                     x1: "0", // start
32078                     y1: (this.height*0.8), // start set the line in 80% of height
32079                     x2: this.width, // end
32080                     y2: (this.height*0.8), // end set the line in 80% of height
32081                     'stroke': "#666",
32082                     'stroke-width': "1",
32083                     'stroke-dasharray': "3",
32084                     'shape-rendering': "crispEdges",
32085                     'pointer-events': "none"
32086                 },
32087                 {
32088                     tag: "path",
32089                     id: this.svgID + "-svg-p",
32090                     'stroke': "navy",
32091                     'stroke-width': "3",
32092                     'fill': "none",
32093                     'pointer-events': 'none'
32094                 }
32095               ]
32096         });
32097         this.createSVG();
32098         this.svgBox = this.svgEl.dom.getScreenCTM();
32099     },
32100     createSVG : function(){ 
32101         var svg = this.signPanel;
32102         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32103         var t = this;
32104
32105         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32106         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32107         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32108         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32109         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32110         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32111         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32112         
32113     },
32114     isTouchEvent : function(e){
32115         return e.type.match(/^touch/);
32116     },
32117     getCoords : function (e) {
32118         var pt    = this.svgEl.dom.createSVGPoint();
32119         pt.x = e.clientX; 
32120         pt.y = e.clientY;
32121         if (this.isTouchEvent(e)) {
32122             pt.x =  e.targetTouches[0].clientX;
32123             pt.y = e.targetTouches[0].clientY;
32124         }
32125         var a = this.svgEl.dom.getScreenCTM();
32126         var b = a.inverse();
32127         var mx = pt.matrixTransform(b);
32128         return mx.x + ',' + mx.y;
32129     },
32130     //mouse event headler 
32131     down : function (e) {
32132         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32133         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32134         
32135         this.isMouseDown = true;
32136         
32137         e.preventDefault();
32138     },
32139     move : function (e) {
32140         if (this.isMouseDown) {
32141             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32142             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32143         }
32144         
32145         e.preventDefault();
32146     },
32147     up : function (e) {
32148         this.isMouseDown = false;
32149         var sp = this.signatureTmp.split(' ');
32150         
32151         if(sp.length > 1){
32152             if(!sp[sp.length-2].match(/^L/)){
32153                 sp.pop();
32154                 sp.pop();
32155                 sp.push("");
32156                 this.signatureTmp = sp.join(" ");
32157             }
32158         }
32159         if(this.getValue() != this.signatureTmp){
32160             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32161             this.isConfirmed = false;
32162         }
32163         e.preventDefault();
32164     },
32165     
32166     /**
32167      * Protected method that will not generally be called directly. It
32168      * is called when the editor creates its toolbar. Override this method if you need to
32169      * add custom toolbar buttons.
32170      * @param {HtmlEditor} editor
32171      */
32172     createToolbar : function(editor){
32173          function btn(id, toggle, handler){
32174             var xid = fid + '-'+ id ;
32175             return {
32176                 id : xid,
32177                 cmd : id,
32178                 cls : 'x-btn-icon x-edit-'+id,
32179                 enableToggle:toggle !== false,
32180                 scope: editor, // was editor...
32181                 handler:handler||editor.relayBtnCmd,
32182                 clickEvent:'mousedown',
32183                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32184                 tabIndex:-1
32185             };
32186         }
32187         
32188         
32189         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32190         this.tb = tb;
32191         this.tb.add(
32192            {
32193                 cls : ' x-signature-btn x-signature-'+id,
32194                 scope: editor, // was editor...
32195                 handler: this.reset,
32196                 clickEvent:'mousedown',
32197                 text: this.labels.clear
32198             },
32199             {
32200                  xtype : 'Fill',
32201                  xns: Roo.Toolbar
32202             }, 
32203             {
32204                 cls : '  x-signature-btn x-signature-'+id,
32205                 scope: editor, // was editor...
32206                 handler: this.confirmHandler,
32207                 clickEvent:'mousedown',
32208                 text: this.labels.confirm
32209             }
32210         );
32211     
32212     },
32213     //public
32214     /**
32215      * when user is clicked confirm then show this image.....
32216      * 
32217      * @return {String} Image Data URI
32218      */
32219     getImageDataURI : function(){
32220         var svg = this.svgEl.dom.parentNode.innerHTML;
32221         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32222         return src; 
32223     },
32224     /**
32225      * 
32226      * @return {Boolean} this.isConfirmed
32227      */
32228     getConfirmed : function(){
32229         return this.isConfirmed;
32230     },
32231     /**
32232      * 
32233      * @return {Number} this.width
32234      */
32235     getWidth : function(){
32236         return this.width;
32237     },
32238     /**
32239      * 
32240      * @return {Number} this.height
32241      */
32242     getHeight : function(){
32243         return this.height;
32244     },
32245     // private
32246     getSignature : function(){
32247         return this.signatureTmp;
32248     },
32249     // private
32250     reset : function(){
32251         this.signatureTmp = '';
32252         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32253         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32254         this.isConfirmed = false;
32255         Roo.form.Signature.superclass.reset.call(this);
32256     },
32257     setSignature : function(s){
32258         this.signatureTmp = s;
32259         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32260         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32261         this.setValue(s);
32262         this.isConfirmed = false;
32263         Roo.form.Signature.superclass.reset.call(this);
32264     }, 
32265     test : function(){
32266 //        Roo.log(this.signPanel.dom.contentWindow.up())
32267     },
32268     //private
32269     setConfirmed : function(){
32270         
32271         
32272         
32273 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32274     },
32275     // private
32276     confirmHandler : function(){
32277         if(!this.getSignature()){
32278             return;
32279         }
32280         
32281         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32282         this.setValue(this.getSignature());
32283         this.isConfirmed = true;
32284         
32285         this.fireEvent('confirm', this);
32286     },
32287     // private
32288     // Subclasses should provide the validation implementation by overriding this
32289     validateValue : function(value){
32290         if(this.allowBlank){
32291             return true;
32292         }
32293         
32294         if(this.isConfirmed){
32295             return true;
32296         }
32297         return false;
32298     }
32299 });/*
32300  * Based on:
32301  * Ext JS Library 1.1.1
32302  * Copyright(c) 2006-2007, Ext JS, LLC.
32303  *
32304  * Originally Released Under LGPL - original licence link has changed is not relivant.
32305  *
32306  * Fork - LGPL
32307  * <script type="text/javascript">
32308  */
32309  
32310
32311 /**
32312  * @class Roo.form.ComboBox
32313  * @extends Roo.form.TriggerField
32314  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32315  * @constructor
32316  * Create a new ComboBox.
32317  * @param {Object} config Configuration options
32318  */
32319 Roo.form.Select = function(config){
32320     Roo.form.Select.superclass.constructor.call(this, config);
32321      
32322 };
32323
32324 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32325     /**
32326      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32327      */
32328     /**
32329      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32330      * rendering into an Roo.Editor, defaults to false)
32331      */
32332     /**
32333      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32334      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32335      */
32336     /**
32337      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32338      */
32339     /**
32340      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32341      * the dropdown list (defaults to undefined, with no header element)
32342      */
32343
32344      /**
32345      * @cfg {String/Roo.Template} tpl The template to use to render the output
32346      */
32347      
32348     // private
32349     defaultAutoCreate : {tag: "select"  },
32350     /**
32351      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32352      */
32353     listWidth: undefined,
32354     /**
32355      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32356      * mode = 'remote' or 'text' if mode = 'local')
32357      */
32358     displayField: undefined,
32359     /**
32360      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32361      * mode = 'remote' or 'value' if mode = 'local'). 
32362      * Note: use of a valueField requires the user make a selection
32363      * in order for a value to be mapped.
32364      */
32365     valueField: undefined,
32366     
32367     
32368     /**
32369      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32370      * field's data value (defaults to the underlying DOM element's name)
32371      */
32372     hiddenName: undefined,
32373     /**
32374      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32375      */
32376     listClass: '',
32377     /**
32378      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32379      */
32380     selectedClass: 'x-combo-selected',
32381     /**
32382      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
32383      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32384      * which displays a downward arrow icon).
32385      */
32386     triggerClass : 'x-form-arrow-trigger',
32387     /**
32388      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32389      */
32390     shadow:'sides',
32391     /**
32392      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32393      * anchor positions (defaults to 'tl-bl')
32394      */
32395     listAlign: 'tl-bl?',
32396     /**
32397      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32398      */
32399     maxHeight: 300,
32400     /**
32401      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
32402      * query specified by the allQuery config option (defaults to 'query')
32403      */
32404     triggerAction: 'query',
32405     /**
32406      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32407      * (defaults to 4, does not apply if editable = false)
32408      */
32409     minChars : 4,
32410     /**
32411      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32412      * delay (typeAheadDelay) if it matches a known value (defaults to false)
32413      */
32414     typeAhead: false,
32415     /**
32416      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32417      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32418      */
32419     queryDelay: 500,
32420     /**
32421      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32422      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
32423      */
32424     pageSize: 0,
32425     /**
32426      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
32427      * when editable = true (defaults to false)
32428      */
32429     selectOnFocus:false,
32430     /**
32431      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32432      */
32433     queryParam: 'query',
32434     /**
32435      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
32436      * when mode = 'remote' (defaults to 'Loading...')
32437      */
32438     loadingText: 'Loading...',
32439     /**
32440      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32441      */
32442     resizable: false,
32443     /**
32444      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32445      */
32446     handleHeight : 8,
32447     /**
32448      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32449      * traditional select (defaults to true)
32450      */
32451     editable: true,
32452     /**
32453      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32454      */
32455     allQuery: '',
32456     /**
32457      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32458      */
32459     mode: 'remote',
32460     /**
32461      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32462      * listWidth has a higher value)
32463      */
32464     minListWidth : 70,
32465     /**
32466      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32467      * allow the user to set arbitrary text into the field (defaults to false)
32468      */
32469     forceSelection:false,
32470     /**
32471      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32472      * if typeAhead = true (defaults to 250)
32473      */
32474     typeAheadDelay : 250,
32475     /**
32476      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32477      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32478      */
32479     valueNotFoundText : undefined,
32480     
32481     /**
32482      * @cfg {String} defaultValue The value displayed after loading the store.
32483      */
32484     defaultValue: '',
32485     
32486     /**
32487      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32488      */
32489     blockFocus : false,
32490     
32491     /**
32492      * @cfg {Boolean} disableClear Disable showing of clear button.
32493      */
32494     disableClear : false,
32495     /**
32496      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
32497      */
32498     alwaysQuery : false,
32499     
32500     //private
32501     addicon : false,
32502     editicon: false,
32503     
32504     // element that contains real text value.. (when hidden is used..)
32505      
32506     // private
32507     onRender : function(ct, position){
32508         Roo.form.Field.prototype.onRender.call(this, ct, position);
32509         
32510         if(this.store){
32511             this.store.on('beforeload', this.onBeforeLoad, this);
32512             this.store.on('load', this.onLoad, this);
32513             this.store.on('loadexception', this.onLoadException, this);
32514             this.store.load({});
32515         }
32516         
32517         
32518         
32519     },
32520
32521     // private
32522     initEvents : function(){
32523         //Roo.form.ComboBox.superclass.initEvents.call(this);
32524  
32525     },
32526
32527     onDestroy : function(){
32528        
32529         if(this.store){
32530             this.store.un('beforeload', this.onBeforeLoad, this);
32531             this.store.un('load', this.onLoad, this);
32532             this.store.un('loadexception', this.onLoadException, this);
32533         }
32534         //Roo.form.ComboBox.superclass.onDestroy.call(this);
32535     },
32536
32537     // private
32538     fireKey : function(e){
32539         if(e.isNavKeyPress() && !this.list.isVisible()){
32540             this.fireEvent("specialkey", this, e);
32541         }
32542     },
32543
32544     // private
32545     onResize: function(w, h){
32546         
32547         return; 
32548     
32549         
32550     },
32551
32552     /**
32553      * Allow or prevent the user from directly editing the field text.  If false is passed,
32554      * the user will only be able to select from the items defined in the dropdown list.  This method
32555      * is the runtime equivalent of setting the 'editable' config option at config time.
32556      * @param {Boolean} value True to allow the user to directly edit the field text
32557      */
32558     setEditable : function(value){
32559          
32560     },
32561
32562     // private
32563     onBeforeLoad : function(){
32564         
32565         Roo.log("Select before load");
32566         return;
32567     
32568         this.innerList.update(this.loadingText ?
32569                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32570         //this.restrictHeight();
32571         this.selectedIndex = -1;
32572     },
32573
32574     // private
32575     onLoad : function(){
32576
32577     
32578         var dom = this.el.dom;
32579         dom.innerHTML = '';
32580          var od = dom.ownerDocument;
32581          
32582         if (this.emptyText) {
32583             var op = od.createElement('option');
32584             op.setAttribute('value', '');
32585             op.innerHTML = String.format('{0}', this.emptyText);
32586             dom.appendChild(op);
32587         }
32588         if(this.store.getCount() > 0){
32589            
32590             var vf = this.valueField;
32591             var df = this.displayField;
32592             this.store.data.each(function(r) {
32593                 // which colmsn to use... testing - cdoe / title..
32594                 var op = od.createElement('option');
32595                 op.setAttribute('value', r.data[vf]);
32596                 op.innerHTML = String.format('{0}', r.data[df]);
32597                 dom.appendChild(op);
32598             });
32599             if (typeof(this.defaultValue != 'undefined')) {
32600                 this.setValue(this.defaultValue);
32601             }
32602             
32603              
32604         }else{
32605             //this.onEmptyResults();
32606         }
32607         //this.el.focus();
32608     },
32609     // private
32610     onLoadException : function()
32611     {
32612         dom.innerHTML = '';
32613             
32614         Roo.log("Select on load exception");
32615         return;
32616     
32617         this.collapse();
32618         Roo.log(this.store.reader.jsonData);
32619         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32620             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32621         }
32622         
32623         
32624     },
32625     // private
32626     onTypeAhead : function(){
32627          
32628     },
32629
32630     // private
32631     onSelect : function(record, index){
32632         Roo.log('on select?');
32633         return;
32634         if(this.fireEvent('beforeselect', this, record, index) !== false){
32635             this.setFromData(index > -1 ? record.data : false);
32636             this.collapse();
32637             this.fireEvent('select', this, record, index);
32638         }
32639     },
32640
32641     /**
32642      * Returns the currently selected field value or empty string if no value is set.
32643      * @return {String} value The selected value
32644      */
32645     getValue : function(){
32646         var dom = this.el.dom;
32647         this.value = dom.options[dom.selectedIndex].value;
32648         return this.value;
32649         
32650     },
32651
32652     /**
32653      * Clears any text/value currently set in the field
32654      */
32655     clearValue : function(){
32656         this.value = '';
32657         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32658         
32659     },
32660
32661     /**
32662      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32663      * will be displayed in the field.  If the value does not match the data value of an existing item,
32664      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32665      * Otherwise the field will be blank (although the value will still be set).
32666      * @param {String} value The value to match
32667      */
32668     setValue : function(v){
32669         var d = this.el.dom;
32670         for (var i =0; i < d.options.length;i++) {
32671             if (v == d.options[i].value) {
32672                 d.selectedIndex = i;
32673                 this.value = v;
32674                 return;
32675             }
32676         }
32677         this.clearValue();
32678     },
32679     /**
32680      * @property {Object} the last set data for the element
32681      */
32682     
32683     lastData : false,
32684     /**
32685      * Sets the value of the field based on a object which is related to the record format for the store.
32686      * @param {Object} value the value to set as. or false on reset?
32687      */
32688     setFromData : function(o){
32689         Roo.log('setfrom data?');
32690          
32691         
32692         
32693     },
32694     // private
32695     reset : function(){
32696         this.clearValue();
32697     },
32698     // private
32699     findRecord : function(prop, value){
32700         
32701         return false;
32702     
32703         var record;
32704         if(this.store.getCount() > 0){
32705             this.store.each(function(r){
32706                 if(r.data[prop] == value){
32707                     record = r;
32708                     return false;
32709                 }
32710                 return true;
32711             });
32712         }
32713         return record;
32714     },
32715     
32716     getName: function()
32717     {
32718         // returns hidden if it's set..
32719         if (!this.rendered) {return ''};
32720         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32721         
32722     },
32723      
32724
32725     
32726
32727     // private
32728     onEmptyResults : function(){
32729         Roo.log('empty results');
32730         //this.collapse();
32731     },
32732
32733     /**
32734      * Returns true if the dropdown list is expanded, else false.
32735      */
32736     isExpanded : function(){
32737         return false;
32738     },
32739
32740     /**
32741      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32742      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32743      * @param {String} value The data value of the item to select
32744      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32745      * selected item if it is not currently in view (defaults to true)
32746      * @return {Boolean} True if the value matched an item in the list, else false
32747      */
32748     selectByValue : function(v, scrollIntoView){
32749         Roo.log('select By Value');
32750         return false;
32751     
32752         if(v !== undefined && v !== null){
32753             var r = this.findRecord(this.valueField || this.displayField, v);
32754             if(r){
32755                 this.select(this.store.indexOf(r), scrollIntoView);
32756                 return true;
32757             }
32758         }
32759         return false;
32760     },
32761
32762     /**
32763      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32764      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32765      * @param {Number} index The zero-based index of the list item to select
32766      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32767      * selected item if it is not currently in view (defaults to true)
32768      */
32769     select : function(index, scrollIntoView){
32770         Roo.log('select ');
32771         return  ;
32772         
32773         this.selectedIndex = index;
32774         this.view.select(index);
32775         if(scrollIntoView !== false){
32776             var el = this.view.getNode(index);
32777             if(el){
32778                 this.innerList.scrollChildIntoView(el, false);
32779             }
32780         }
32781     },
32782
32783       
32784
32785     // private
32786     validateBlur : function(){
32787         
32788         return;
32789         
32790     },
32791
32792     // private
32793     initQuery : function(){
32794         this.doQuery(this.getRawValue());
32795     },
32796
32797     // private
32798     doForce : function(){
32799         if(this.el.dom.value.length > 0){
32800             this.el.dom.value =
32801                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32802              
32803         }
32804     },
32805
32806     /**
32807      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32808      * query allowing the query action to be canceled if needed.
32809      * @param {String} query The SQL query to execute
32810      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32811      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32812      * saved in the current store (defaults to false)
32813      */
32814     doQuery : function(q, forceAll){
32815         
32816         Roo.log('doQuery?');
32817         if(q === undefined || q === null){
32818             q = '';
32819         }
32820         var qe = {
32821             query: q,
32822             forceAll: forceAll,
32823             combo: this,
32824             cancel:false
32825         };
32826         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32827             return false;
32828         }
32829         q = qe.query;
32830         forceAll = qe.forceAll;
32831         if(forceAll === true || (q.length >= this.minChars)){
32832             if(this.lastQuery != q || this.alwaysQuery){
32833                 this.lastQuery = q;
32834                 if(this.mode == 'local'){
32835                     this.selectedIndex = -1;
32836                     if(forceAll){
32837                         this.store.clearFilter();
32838                     }else{
32839                         this.store.filter(this.displayField, q);
32840                     }
32841                     this.onLoad();
32842                 }else{
32843                     this.store.baseParams[this.queryParam] = q;
32844                     this.store.load({
32845                         params: this.getParams(q)
32846                     });
32847                     this.expand();
32848                 }
32849             }else{
32850                 this.selectedIndex = -1;
32851                 this.onLoad();   
32852             }
32853         }
32854     },
32855
32856     // private
32857     getParams : function(q){
32858         var p = {};
32859         //p[this.queryParam] = q;
32860         if(this.pageSize){
32861             p.start = 0;
32862             p.limit = this.pageSize;
32863         }
32864         return p;
32865     },
32866
32867     /**
32868      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32869      */
32870     collapse : function(){
32871         
32872     },
32873
32874     // private
32875     collapseIf : function(e){
32876         
32877     },
32878
32879     /**
32880      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32881      */
32882     expand : function(){
32883         
32884     } ,
32885
32886     // private
32887      
32888
32889     /** 
32890     * @cfg {Boolean} grow 
32891     * @hide 
32892     */
32893     /** 
32894     * @cfg {Number} growMin 
32895     * @hide 
32896     */
32897     /** 
32898     * @cfg {Number} growMax 
32899     * @hide 
32900     */
32901     /**
32902      * @hide
32903      * @method autoSize
32904      */
32905     
32906     setWidth : function()
32907     {
32908         
32909     },
32910     getResizeEl : function(){
32911         return this.el;
32912     }
32913 });//<script type="text/javasscript">
32914  
32915
32916 /**
32917  * @class Roo.DDView
32918  * A DnD enabled version of Roo.View.
32919  * @param {Element/String} container The Element in which to create the View.
32920  * @param {String} tpl The template string used to create the markup for each element of the View
32921  * @param {Object} config The configuration properties. These include all the config options of
32922  * {@link Roo.View} plus some specific to this class.<br>
32923  * <p>
32924  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32925  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32926  * <p>
32927  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32928 .x-view-drag-insert-above {
32929         border-top:1px dotted #3366cc;
32930 }
32931 .x-view-drag-insert-below {
32932         border-bottom:1px dotted #3366cc;
32933 }
32934 </code></pre>
32935  * 
32936  */
32937  
32938 Roo.DDView = function(container, tpl, config) {
32939     Roo.DDView.superclass.constructor.apply(this, arguments);
32940     this.getEl().setStyle("outline", "0px none");
32941     this.getEl().unselectable();
32942     if (this.dragGroup) {
32943         this.setDraggable(this.dragGroup.split(","));
32944     }
32945     if (this.dropGroup) {
32946         this.setDroppable(this.dropGroup.split(","));
32947     }
32948     if (this.deletable) {
32949         this.setDeletable();
32950     }
32951     this.isDirtyFlag = false;
32952         this.addEvents({
32953                 "drop" : true
32954         });
32955 };
32956
32957 Roo.extend(Roo.DDView, Roo.View, {
32958 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32959 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32960 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32961 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32962
32963         isFormField: true,
32964
32965         reset: Roo.emptyFn,
32966         
32967         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32968
32969         validate: function() {
32970                 return true;
32971         },
32972         
32973         destroy: function() {
32974                 this.purgeListeners();
32975                 this.getEl.removeAllListeners();
32976                 this.getEl().remove();
32977                 if (this.dragZone) {
32978                         if (this.dragZone.destroy) {
32979                                 this.dragZone.destroy();
32980                         }
32981                 }
32982                 if (this.dropZone) {
32983                         if (this.dropZone.destroy) {
32984                                 this.dropZone.destroy();
32985                         }
32986                 }
32987         },
32988
32989 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32990         getName: function() {
32991                 return this.name;
32992         },
32993
32994 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32995         setValue: function(v) {
32996                 if (!this.store) {
32997                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32998                 }
32999                 var data = {};
33000                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
33001                 this.store.proxy = new Roo.data.MemoryProxy(data);
33002                 this.store.load();
33003         },
33004
33005 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
33006         getValue: function() {
33007                 var result = '(';
33008                 this.store.each(function(rec) {
33009                         result += rec.id + ',';
33010                 });
33011                 return result.substr(0, result.length - 1) + ')';
33012         },
33013         
33014         getIds: function() {
33015                 var i = 0, result = new Array(this.store.getCount());
33016                 this.store.each(function(rec) {
33017                         result[i++] = rec.id;
33018                 });
33019                 return result;
33020         },
33021         
33022         isDirty: function() {
33023                 return this.isDirtyFlag;
33024         },
33025
33026 /**
33027  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
33028  *      whole Element becomes the target, and this causes the drop gesture to append.
33029  */
33030     getTargetFromEvent : function(e) {
33031                 var target = e.getTarget();
33032                 while ((target !== null) && (target.parentNode != this.el.dom)) {
33033                 target = target.parentNode;
33034                 }
33035                 if (!target) {
33036                         target = this.el.dom.lastChild || this.el.dom;
33037                 }
33038                 return target;
33039     },
33040
33041 /**
33042  *      Create the drag data which consists of an object which has the property "ddel" as
33043  *      the drag proxy element. 
33044  */
33045     getDragData : function(e) {
33046         var target = this.findItemFromChild(e.getTarget());
33047                 if(target) {
33048                         this.handleSelection(e);
33049                         var selNodes = this.getSelectedNodes();
33050             var dragData = {
33051                 source: this,
33052                 copy: this.copy || (this.allowCopy && e.ctrlKey),
33053                 nodes: selNodes,
33054                 records: []
33055                         };
33056                         var selectedIndices = this.getSelectedIndexes();
33057                         for (var i = 0; i < selectedIndices.length; i++) {
33058                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
33059                         }
33060                         if (selNodes.length == 1) {
33061                                 dragData.ddel = target.cloneNode(true); // the div element
33062                         } else {
33063                                 var div = document.createElement('div'); // create the multi element drag "ghost"
33064                                 div.className = 'multi-proxy';
33065                                 for (var i = 0, len = selNodes.length; i < len; i++) {
33066                                         div.appendChild(selNodes[i].cloneNode(true));
33067                                 }
33068                                 dragData.ddel = div;
33069                         }
33070             //console.log(dragData)
33071             //console.log(dragData.ddel.innerHTML)
33072                         return dragData;
33073                 }
33074         //console.log('nodragData')
33075                 return false;
33076     },
33077     
33078 /**     Specify to which ddGroup items in this DDView may be dragged. */
33079     setDraggable: function(ddGroup) {
33080         if (ddGroup instanceof Array) {
33081                 Roo.each(ddGroup, this.setDraggable, this);
33082                 return;
33083         }
33084         if (this.dragZone) {
33085                 this.dragZone.addToGroup(ddGroup);
33086         } else {
33087                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33088                                 containerScroll: true,
33089                                 ddGroup: ddGroup 
33090
33091                         });
33092 //                      Draggability implies selection. DragZone's mousedown selects the element.
33093                         if (!this.multiSelect) { this.singleSelect = true; }
33094
33095 //                      Wire the DragZone's handlers up to methods in *this*
33096                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
33097                 }
33098     },
33099
33100 /**     Specify from which ddGroup this DDView accepts drops. */
33101     setDroppable: function(ddGroup) {
33102         if (ddGroup instanceof Array) {
33103                 Roo.each(ddGroup, this.setDroppable, this);
33104                 return;
33105         }
33106         if (this.dropZone) {
33107                 this.dropZone.addToGroup(ddGroup);
33108         } else {
33109                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33110                                 containerScroll: true,
33111                                 ddGroup: ddGroup
33112                         });
33113
33114 //                      Wire the DropZone's handlers up to methods in *this*
33115                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33116                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33117                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33118                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33119                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33120                 }
33121     },
33122
33123 /**     Decide whether to drop above or below a View node. */
33124     getDropPoint : function(e, n, dd){
33125         if (n == this.el.dom) { return "above"; }
33126                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33127                 var c = t + (b - t) / 2;
33128                 var y = Roo.lib.Event.getPageY(e);
33129                 if(y <= c) {
33130                         return "above";
33131                 }else{
33132                         return "below";
33133                 }
33134     },
33135
33136     onNodeEnter : function(n, dd, e, data){
33137                 return false;
33138     },
33139     
33140     onNodeOver : function(n, dd, e, data){
33141                 var pt = this.getDropPoint(e, n, dd);
33142                 // set the insert point style on the target node
33143                 var dragElClass = this.dropNotAllowed;
33144                 if (pt) {
33145                         var targetElClass;
33146                         if (pt == "above"){
33147                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33148                                 targetElClass = "x-view-drag-insert-above";
33149                         } else {
33150                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33151                                 targetElClass = "x-view-drag-insert-below";
33152                         }
33153                         if (this.lastInsertClass != targetElClass){
33154                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33155                                 this.lastInsertClass = targetElClass;
33156                         }
33157                 }
33158                 return dragElClass;
33159         },
33160
33161     onNodeOut : function(n, dd, e, data){
33162                 this.removeDropIndicators(n);
33163     },
33164
33165     onNodeDrop : function(n, dd, e, data){
33166         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33167                 return false;
33168         }
33169         var pt = this.getDropPoint(e, n, dd);
33170                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33171                 if (pt == "below") { insertAt++; }
33172                 for (var i = 0; i < data.records.length; i++) {
33173                         var r = data.records[i];
33174                         var dup = this.store.getById(r.id);
33175                         if (dup && (dd != this.dragZone)) {
33176                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33177                         } else {
33178                                 if (data.copy) {
33179                                         this.store.insert(insertAt++, r.copy());
33180                                 } else {
33181                                         data.source.isDirtyFlag = true;
33182                                         r.store.remove(r);
33183                                         this.store.insert(insertAt++, r);
33184                                 }
33185                                 this.isDirtyFlag = true;
33186                         }
33187                 }
33188                 this.dragZone.cachedTarget = null;
33189                 return true;
33190     },
33191
33192     removeDropIndicators : function(n){
33193                 if(n){
33194                         Roo.fly(n).removeClass([
33195                                 "x-view-drag-insert-above",
33196                                 "x-view-drag-insert-below"]);
33197                         this.lastInsertClass = "_noclass";
33198                 }
33199     },
33200
33201 /**
33202  *      Utility method. Add a delete option to the DDView's context menu.
33203  *      @param {String} imageUrl The URL of the "delete" icon image.
33204  */
33205         setDeletable: function(imageUrl) {
33206                 if (!this.singleSelect && !this.multiSelect) {
33207                         this.singleSelect = true;
33208                 }
33209                 var c = this.getContextMenu();
33210                 this.contextMenu.on("itemclick", function(item) {
33211                         switch (item.id) {
33212                                 case "delete":
33213                                         this.remove(this.getSelectedIndexes());
33214                                         break;
33215                         }
33216                 }, this);
33217                 this.contextMenu.add({
33218                         icon: imageUrl,
33219                         id: "delete",
33220                         text: 'Delete'
33221                 });
33222         },
33223         
33224 /**     Return the context menu for this DDView. */
33225         getContextMenu: function() {
33226                 if (!this.contextMenu) {
33227 //                      Create the View's context menu
33228                         this.contextMenu = new Roo.menu.Menu({
33229                                 id: this.id + "-contextmenu"
33230                         });
33231                         this.el.on("contextmenu", this.showContextMenu, this);
33232                 }
33233                 return this.contextMenu;
33234         },
33235         
33236         disableContextMenu: function() {
33237                 if (this.contextMenu) {
33238                         this.el.un("contextmenu", this.showContextMenu, this);
33239                 }
33240         },
33241
33242         showContextMenu: function(e, item) {
33243         item = this.findItemFromChild(e.getTarget());
33244                 if (item) {
33245                         e.stopEvent();
33246                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33247                         this.contextMenu.showAt(e.getXY());
33248             }
33249     },
33250
33251 /**
33252  *      Remove {@link Roo.data.Record}s at the specified indices.
33253  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33254  */
33255     remove: function(selectedIndices) {
33256                 selectedIndices = [].concat(selectedIndices);
33257                 for (var i = 0; i < selectedIndices.length; i++) {
33258                         var rec = this.store.getAt(selectedIndices[i]);
33259                         this.store.remove(rec);
33260                 }
33261     },
33262
33263 /**
33264  *      Double click fires the event, but also, if this is draggable, and there is only one other
33265  *      related DropZone, it transfers the selected node.
33266  */
33267     onDblClick : function(e){
33268         var item = this.findItemFromChild(e.getTarget());
33269         if(item){
33270             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33271                 return false;
33272             }
33273             if (this.dragGroup) {
33274                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33275                     while (targets.indexOf(this.dropZone) > -1) {
33276                             targets.remove(this.dropZone);
33277                                 }
33278                     if (targets.length == 1) {
33279                                         this.dragZone.cachedTarget = null;
33280                         var el = Roo.get(targets[0].getEl());
33281                         var box = el.getBox(true);
33282                         targets[0].onNodeDrop(el.dom, {
33283                                 target: el.dom,
33284                                 xy: [box.x, box.y + box.height - 1]
33285                         }, null, this.getDragData(e));
33286                     }
33287                 }
33288         }
33289     },
33290     
33291     handleSelection: function(e) {
33292                 this.dragZone.cachedTarget = null;
33293         var item = this.findItemFromChild(e.getTarget());
33294         if (!item) {
33295                 this.clearSelections(true);
33296                 return;
33297         }
33298                 if (item && (this.multiSelect || this.singleSelect)){
33299                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33300                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33301                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33302                                 this.unselect(item);
33303                         } else {
33304                                 this.select(item, this.multiSelect && e.ctrlKey);
33305                                 this.lastSelection = item;
33306                         }
33307                 }
33308     },
33309
33310     onItemClick : function(item, index, e){
33311                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33312                         return false;
33313                 }
33314                 return true;
33315     },
33316
33317     unselect : function(nodeInfo, suppressEvent){
33318                 var node = this.getNode(nodeInfo);
33319                 if(node && this.isSelected(node)){
33320                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33321                                 Roo.fly(node).removeClass(this.selectedClass);
33322                                 this.selections.remove(node);
33323                                 if(!suppressEvent){
33324                                         this.fireEvent("selectionchange", this, this.selections);
33325                                 }
33326                         }
33327                 }
33328     }
33329 });
33330 /*
33331  * Based on:
33332  * Ext JS Library 1.1.1
33333  * Copyright(c) 2006-2007, Ext JS, LLC.
33334  *
33335  * Originally Released Under LGPL - original licence link has changed is not relivant.
33336  *
33337  * Fork - LGPL
33338  * <script type="text/javascript">
33339  */
33340  
33341 /**
33342  * @class Roo.LayoutManager
33343  * @extends Roo.util.Observable
33344  * Base class for layout managers.
33345  */
33346 Roo.LayoutManager = function(container, config){
33347     Roo.LayoutManager.superclass.constructor.call(this);
33348     this.el = Roo.get(container);
33349     // ie scrollbar fix
33350     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33351         document.body.scroll = "no";
33352     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33353         this.el.position('relative');
33354     }
33355     this.id = this.el.id;
33356     this.el.addClass("x-layout-container");
33357     /** false to disable window resize monitoring @type Boolean */
33358     this.monitorWindowResize = true;
33359     this.regions = {};
33360     this.addEvents({
33361         /**
33362          * @event layout
33363          * Fires when a layout is performed. 
33364          * @param {Roo.LayoutManager} this
33365          */
33366         "layout" : true,
33367         /**
33368          * @event regionresized
33369          * Fires when the user resizes a region. 
33370          * @param {Roo.LayoutRegion} region The resized region
33371          * @param {Number} newSize The new size (width for east/west, height for north/south)
33372          */
33373         "regionresized" : true,
33374         /**
33375          * @event regioncollapsed
33376          * Fires when a region is collapsed. 
33377          * @param {Roo.LayoutRegion} region The collapsed region
33378          */
33379         "regioncollapsed" : true,
33380         /**
33381          * @event regionexpanded
33382          * Fires when a region is expanded.  
33383          * @param {Roo.LayoutRegion} region The expanded region
33384          */
33385         "regionexpanded" : true
33386     });
33387     this.updating = false;
33388     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33389 };
33390
33391 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33392     /**
33393      * Returns true if this layout is currently being updated
33394      * @return {Boolean}
33395      */
33396     isUpdating : function(){
33397         return this.updating; 
33398     },
33399     
33400     /**
33401      * Suspend the LayoutManager from doing auto-layouts while
33402      * making multiple add or remove calls
33403      */
33404     beginUpdate : function(){
33405         this.updating = true;    
33406     },
33407     
33408     /**
33409      * Restore auto-layouts and optionally disable the manager from performing a layout
33410      * @param {Boolean} noLayout true to disable a layout update 
33411      */
33412     endUpdate : function(noLayout){
33413         this.updating = false;
33414         if(!noLayout){
33415             this.layout();
33416         }    
33417     },
33418     
33419     layout: function(){
33420         
33421     },
33422     
33423     onRegionResized : function(region, newSize){
33424         this.fireEvent("regionresized", region, newSize);
33425         this.layout();
33426     },
33427     
33428     onRegionCollapsed : function(region){
33429         this.fireEvent("regioncollapsed", region);
33430     },
33431     
33432     onRegionExpanded : function(region){
33433         this.fireEvent("regionexpanded", region);
33434     },
33435         
33436     /**
33437      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33438      * performs box-model adjustments.
33439      * @return {Object} The size as an object {width: (the width), height: (the height)}
33440      */
33441     getViewSize : function(){
33442         var size;
33443         if(this.el.dom != document.body){
33444             size = this.el.getSize();
33445         }else{
33446             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33447         }
33448         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33449         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33450         return size;
33451     },
33452     
33453     /**
33454      * Returns the Element this layout is bound to.
33455      * @return {Roo.Element}
33456      */
33457     getEl : function(){
33458         return this.el;
33459     },
33460     
33461     /**
33462      * Returns the specified region.
33463      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33464      * @return {Roo.LayoutRegion}
33465      */
33466     getRegion : function(target){
33467         return this.regions[target.toLowerCase()];
33468     },
33469     
33470     onWindowResize : function(){
33471         if(this.monitorWindowResize){
33472             this.layout();
33473         }
33474     }
33475 });/*
33476  * Based on:
33477  * Ext JS Library 1.1.1
33478  * Copyright(c) 2006-2007, Ext JS, LLC.
33479  *
33480  * Originally Released Under LGPL - original licence link has changed is not relivant.
33481  *
33482  * Fork - LGPL
33483  * <script type="text/javascript">
33484  */
33485 /**
33486  * @class Roo.BorderLayout
33487  * @extends Roo.LayoutManager
33488  * @children Roo.ContentPanel
33489  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33490  * please see: <br><br>
33491  * <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>
33492  * <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>
33493  * Example:
33494  <pre><code>
33495  var layout = new Roo.BorderLayout(document.body, {
33496     north: {
33497         initialSize: 25,
33498         titlebar: false
33499     },
33500     west: {
33501         split:true,
33502         initialSize: 200,
33503         minSize: 175,
33504         maxSize: 400,
33505         titlebar: true,
33506         collapsible: true
33507     },
33508     east: {
33509         split:true,
33510         initialSize: 202,
33511         minSize: 175,
33512         maxSize: 400,
33513         titlebar: true,
33514         collapsible: true
33515     },
33516     south: {
33517         split:true,
33518         initialSize: 100,
33519         minSize: 100,
33520         maxSize: 200,
33521         titlebar: true,
33522         collapsible: true
33523     },
33524     center: {
33525         titlebar: true,
33526         autoScroll:true,
33527         resizeTabs: true,
33528         minTabWidth: 50,
33529         preferredTabWidth: 150
33530     }
33531 });
33532
33533 // shorthand
33534 var CP = Roo.ContentPanel;
33535
33536 layout.beginUpdate();
33537 layout.add("north", new CP("north", "North"));
33538 layout.add("south", new CP("south", {title: "South", closable: true}));
33539 layout.add("west", new CP("west", {title: "West"}));
33540 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33541 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33542 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33543 layout.getRegion("center").showPanel("center1");
33544 layout.endUpdate();
33545 </code></pre>
33546
33547 <b>The container the layout is rendered into can be either the body element or any other element.
33548 If it is not the body element, the container needs to either be an absolute positioned element,
33549 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33550 the container size if it is not the body element.</b>
33551
33552 * @constructor
33553 * Create a new BorderLayout
33554 * @param {String/HTMLElement/Element} container The container this layout is bound to
33555 * @param {Object} config Configuration options
33556  */
33557 Roo.BorderLayout = function(container, config){
33558     config = config || {};
33559     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33560     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33561     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33562         var target = this.factory.validRegions[i];
33563         if(config[target]){
33564             this.addRegion(target, config[target]);
33565         }
33566     }
33567 };
33568
33569 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33570         
33571         /**
33572          * @cfg {Roo.LayoutRegion} east
33573          */
33574         /**
33575          * @cfg {Roo.LayoutRegion} west
33576          */
33577         /**
33578          * @cfg {Roo.LayoutRegion} north
33579          */
33580         /**
33581          * @cfg {Roo.LayoutRegion} south
33582          */
33583         /**
33584          * @cfg {Roo.LayoutRegion} center
33585          */
33586     /**
33587      * Creates and adds a new region if it doesn't already exist.
33588      * @param {String} target The target region key (north, south, east, west or center).
33589      * @param {Object} config The regions config object
33590      * @return {BorderLayoutRegion} The new region
33591      */
33592     addRegion : function(target, config){
33593         if(!this.regions[target]){
33594             var r = this.factory.create(target, this, config);
33595             this.bindRegion(target, r);
33596         }
33597         return this.regions[target];
33598     },
33599
33600     // private (kinda)
33601     bindRegion : function(name, r){
33602         this.regions[name] = r;
33603         r.on("visibilitychange", this.layout, this);
33604         r.on("paneladded", this.layout, this);
33605         r.on("panelremoved", this.layout, this);
33606         r.on("invalidated", this.layout, this);
33607         r.on("resized", this.onRegionResized, this);
33608         r.on("collapsed", this.onRegionCollapsed, this);
33609         r.on("expanded", this.onRegionExpanded, this);
33610     },
33611
33612     /**
33613      * Performs a layout update.
33614      */
33615     layout : function(){
33616         if(this.updating) {
33617             return;
33618         }
33619         var size = this.getViewSize();
33620         var w = size.width;
33621         var h = size.height;
33622         var centerW = w;
33623         var centerH = h;
33624         var centerY = 0;
33625         var centerX = 0;
33626         //var x = 0, y = 0;
33627
33628         var rs = this.regions;
33629         var north = rs["north"];
33630         var south = rs["south"]; 
33631         var west = rs["west"];
33632         var east = rs["east"];
33633         var center = rs["center"];
33634         //if(this.hideOnLayout){ // not supported anymore
33635             //c.el.setStyle("display", "none");
33636         //}
33637         if(north && north.isVisible()){
33638             var b = north.getBox();
33639             var m = north.getMargins();
33640             b.width = w - (m.left+m.right);
33641             b.x = m.left;
33642             b.y = m.top;
33643             centerY = b.height + b.y + m.bottom;
33644             centerH -= centerY;
33645             north.updateBox(this.safeBox(b));
33646         }
33647         if(south && south.isVisible()){
33648             var b = south.getBox();
33649             var m = south.getMargins();
33650             b.width = w - (m.left+m.right);
33651             b.x = m.left;
33652             var totalHeight = (b.height + m.top + m.bottom);
33653             b.y = h - totalHeight + m.top;
33654             centerH -= totalHeight;
33655             south.updateBox(this.safeBox(b));
33656         }
33657         if(west && west.isVisible()){
33658             var b = west.getBox();
33659             var m = west.getMargins();
33660             b.height = centerH - (m.top+m.bottom);
33661             b.x = m.left;
33662             b.y = centerY + m.top;
33663             var totalWidth = (b.width + m.left + m.right);
33664             centerX += totalWidth;
33665             centerW -= totalWidth;
33666             west.updateBox(this.safeBox(b));
33667         }
33668         if(east && east.isVisible()){
33669             var b = east.getBox();
33670             var m = east.getMargins();
33671             b.height = centerH - (m.top+m.bottom);
33672             var totalWidth = (b.width + m.left + m.right);
33673             b.x = w - totalWidth + m.left;
33674             b.y = centerY + m.top;
33675             centerW -= totalWidth;
33676             east.updateBox(this.safeBox(b));
33677         }
33678         if(center){
33679             var m = center.getMargins();
33680             var centerBox = {
33681                 x: centerX + m.left,
33682                 y: centerY + m.top,
33683                 width: centerW - (m.left+m.right),
33684                 height: centerH - (m.top+m.bottom)
33685             };
33686             //if(this.hideOnLayout){
33687                 //center.el.setStyle("display", "block");
33688             //}
33689             center.updateBox(this.safeBox(centerBox));
33690         }
33691         this.el.repaint();
33692         this.fireEvent("layout", this);
33693     },
33694
33695     // private
33696     safeBox : function(box){
33697         box.width = Math.max(0, box.width);
33698         box.height = Math.max(0, box.height);
33699         return box;
33700     },
33701
33702     /**
33703      * Adds a ContentPanel (or subclass) to this layout.
33704      * @param {String} target The target region key (north, south, east, west or center).
33705      * @param {Roo.ContentPanel} panel The panel to add
33706      * @return {Roo.ContentPanel} The added panel
33707      */
33708     add : function(target, panel){
33709          
33710         target = target.toLowerCase();
33711         return this.regions[target].add(panel);
33712     },
33713
33714     /**
33715      * Remove a ContentPanel (or subclass) to this layout.
33716      * @param {String} target The target region key (north, south, east, west or center).
33717      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33718      * @return {Roo.ContentPanel} The removed panel
33719      */
33720     remove : function(target, panel){
33721         target = target.toLowerCase();
33722         return this.regions[target].remove(panel);
33723     },
33724
33725     /**
33726      * Searches all regions for a panel with the specified id
33727      * @param {String} panelId
33728      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33729      */
33730     findPanel : function(panelId){
33731         var rs = this.regions;
33732         for(var target in rs){
33733             if(typeof rs[target] != "function"){
33734                 var p = rs[target].getPanel(panelId);
33735                 if(p){
33736                     return p;
33737                 }
33738             }
33739         }
33740         return null;
33741     },
33742
33743     /**
33744      * Searches all regions for a panel with the specified id and activates (shows) it.
33745      * @param {String/ContentPanel} panelId The panels id or the panel itself
33746      * @return {Roo.ContentPanel} The shown panel or null
33747      */
33748     showPanel : function(panelId) {
33749       var rs = this.regions;
33750       for(var target in rs){
33751          var r = rs[target];
33752          if(typeof r != "function"){
33753             if(r.hasPanel(panelId)){
33754                return r.showPanel(panelId);
33755             }
33756          }
33757       }
33758       return null;
33759    },
33760
33761    /**
33762      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33763      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33764      */
33765     restoreState : function(provider){
33766         if(!provider){
33767             provider = Roo.state.Manager;
33768         }
33769         var sm = new Roo.LayoutStateManager();
33770         sm.init(this, provider);
33771     },
33772
33773     /**
33774      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33775      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33776      * a valid ContentPanel config object.  Example:
33777      * <pre><code>
33778 // Create the main layout
33779 var layout = new Roo.BorderLayout('main-ct', {
33780     west: {
33781         split:true,
33782         minSize: 175,
33783         titlebar: true
33784     },
33785     center: {
33786         title:'Components'
33787     }
33788 }, 'main-ct');
33789
33790 // Create and add multiple ContentPanels at once via configs
33791 layout.batchAdd({
33792    west: {
33793        id: 'source-files',
33794        autoCreate:true,
33795        title:'Ext Source Files',
33796        autoScroll:true,
33797        fitToFrame:true
33798    },
33799    center : {
33800        el: cview,
33801        autoScroll:true,
33802        fitToFrame:true,
33803        toolbar: tb,
33804        resizeEl:'cbody'
33805    }
33806 });
33807 </code></pre>
33808      * @param {Object} regions An object containing ContentPanel configs by region name
33809      */
33810     batchAdd : function(regions){
33811         this.beginUpdate();
33812         for(var rname in regions){
33813             var lr = this.regions[rname];
33814             if(lr){
33815                 this.addTypedPanels(lr, regions[rname]);
33816             }
33817         }
33818         this.endUpdate();
33819     },
33820
33821     // private
33822     addTypedPanels : function(lr, ps){
33823         if(typeof ps == 'string'){
33824             lr.add(new Roo.ContentPanel(ps));
33825         }
33826         else if(ps instanceof Array){
33827             for(var i =0, len = ps.length; i < len; i++){
33828                 this.addTypedPanels(lr, ps[i]);
33829             }
33830         }
33831         else if(!ps.events){ // raw config?
33832             var el = ps.el;
33833             delete ps.el; // prevent conflict
33834             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33835         }
33836         else {  // panel object assumed!
33837             lr.add(ps);
33838         }
33839     },
33840     /**
33841      * Adds a xtype elements to the layout.
33842      * <pre><code>
33843
33844 layout.addxtype({
33845        xtype : 'ContentPanel',
33846        region: 'west',
33847        items: [ .... ]
33848    }
33849 );
33850
33851 layout.addxtype({
33852         xtype : 'NestedLayoutPanel',
33853         region: 'west',
33854         layout: {
33855            center: { },
33856            west: { }   
33857         },
33858         items : [ ... list of content panels or nested layout panels.. ]
33859    }
33860 );
33861 </code></pre>
33862      * @param {Object} cfg Xtype definition of item to add.
33863      */
33864     addxtype : function(cfg)
33865     {
33866         // basically accepts a pannel...
33867         // can accept a layout region..!?!?
33868         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33869         
33870         if (!cfg.xtype.match(/Panel$/)) {
33871             return false;
33872         }
33873         var ret = false;
33874         
33875         if (typeof(cfg.region) == 'undefined') {
33876             Roo.log("Failed to add Panel, region was not set");
33877             Roo.log(cfg);
33878             return false;
33879         }
33880         var region = cfg.region;
33881         delete cfg.region;
33882         
33883           
33884         var xitems = [];
33885         if (cfg.items) {
33886             xitems = cfg.items;
33887             delete cfg.items;
33888         }
33889         var nb = false;
33890         
33891         switch(cfg.xtype) 
33892         {
33893             case 'ContentPanel':  // ContentPanel (el, cfg)
33894             case 'ScrollPanel':  // ContentPanel (el, cfg)
33895             case 'ViewPanel': 
33896                 if(cfg.autoCreate) {
33897                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33898                 } else {
33899                     var el = this.el.createChild();
33900                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33901                 }
33902                 
33903                 this.add(region, ret);
33904                 break;
33905             
33906             
33907             case 'TreePanel': // our new panel!
33908                 cfg.el = this.el.createChild();
33909                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33910                 this.add(region, ret);
33911                 break;
33912             
33913             case 'NestedLayoutPanel': 
33914                 // create a new Layout (which is  a Border Layout...
33915                 var el = this.el.createChild();
33916                 var clayout = cfg.layout;
33917                 delete cfg.layout;
33918                 clayout.items   = clayout.items  || [];
33919                 // replace this exitems with the clayout ones..
33920                 xitems = clayout.items;
33921                  
33922                 
33923                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33924                     cfg.background = false;
33925                 }
33926                 var layout = new Roo.BorderLayout(el, clayout);
33927                 
33928                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33929                 //console.log('adding nested layout panel '  + cfg.toSource());
33930                 this.add(region, ret);
33931                 nb = {}; /// find first...
33932                 break;
33933                 
33934             case 'GridPanel': 
33935             
33936                 // needs grid and region
33937                 
33938                 //var el = this.getRegion(region).el.createChild();
33939                 var el = this.el.createChild();
33940                 // create the grid first...
33941                 
33942                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33943                 delete cfg.grid;
33944                 if (region == 'center' && this.active ) {
33945                     cfg.background = false;
33946                 }
33947                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33948                 
33949                 this.add(region, ret);
33950                 if (cfg.background) {
33951                     ret.on('activate', function(gp) {
33952                         if (!gp.grid.rendered) {
33953                             gp.grid.render();
33954                         }
33955                     });
33956                 } else {
33957                     grid.render();
33958                 }
33959                 break;
33960            
33961            
33962            
33963                 
33964                 
33965                 
33966             default:
33967                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33968                     
33969                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33970                     this.add(region, ret);
33971                 } else {
33972                 
33973                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33974                     return null;
33975                 }
33976                 
33977              // GridPanel (grid, cfg)
33978             
33979         }
33980         this.beginUpdate();
33981         // add children..
33982         var region = '';
33983         var abn = {};
33984         Roo.each(xitems, function(i)  {
33985             region = nb && i.region ? i.region : false;
33986             
33987             var add = ret.addxtype(i);
33988            
33989             if (region) {
33990                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33991                 if (!i.background) {
33992                     abn[region] = nb[region] ;
33993                 }
33994             }
33995             
33996         });
33997         this.endUpdate();
33998
33999         // make the last non-background panel active..
34000         //if (nb) { Roo.log(abn); }
34001         if (nb) {
34002             
34003             for(var r in abn) {
34004                 region = this.getRegion(r);
34005                 if (region) {
34006                     // tried using nb[r], but it does not work..
34007                      
34008                     region.showPanel(abn[r]);
34009                    
34010                 }
34011             }
34012         }
34013         return ret;
34014         
34015     }
34016 });
34017
34018 /**
34019  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
34020  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
34021  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
34022  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
34023  * <pre><code>
34024 // shorthand
34025 var CP = Roo.ContentPanel;
34026
34027 var layout = Roo.BorderLayout.create({
34028     north: {
34029         initialSize: 25,
34030         titlebar: false,
34031         panels: [new CP("north", "North")]
34032     },
34033     west: {
34034         split:true,
34035         initialSize: 200,
34036         minSize: 175,
34037         maxSize: 400,
34038         titlebar: true,
34039         collapsible: true,
34040         panels: [new CP("west", {title: "West"})]
34041     },
34042     east: {
34043         split:true,
34044         initialSize: 202,
34045         minSize: 175,
34046         maxSize: 400,
34047         titlebar: true,
34048         collapsible: true,
34049         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34050     },
34051     south: {
34052         split:true,
34053         initialSize: 100,
34054         minSize: 100,
34055         maxSize: 200,
34056         titlebar: true,
34057         collapsible: true,
34058         panels: [new CP("south", {title: "South", closable: true})]
34059     },
34060     center: {
34061         titlebar: true,
34062         autoScroll:true,
34063         resizeTabs: true,
34064         minTabWidth: 50,
34065         preferredTabWidth: 150,
34066         panels: [
34067             new CP("center1", {title: "Close Me", closable: true}),
34068             new CP("center2", {title: "Center Panel", closable: false})
34069         ]
34070     }
34071 }, document.body);
34072
34073 layout.getRegion("center").showPanel("center1");
34074 </code></pre>
34075  * @param config
34076  * @param targetEl
34077  */
34078 Roo.BorderLayout.create = function(config, targetEl){
34079     var layout = new Roo.BorderLayout(targetEl || document.body, config);
34080     layout.beginUpdate();
34081     var regions = Roo.BorderLayout.RegionFactory.validRegions;
34082     for(var j = 0, jlen = regions.length; j < jlen; j++){
34083         var lr = regions[j];
34084         if(layout.regions[lr] && config[lr].panels){
34085             var r = layout.regions[lr];
34086             var ps = config[lr].panels;
34087             layout.addTypedPanels(r, ps);
34088         }
34089     }
34090     layout.endUpdate();
34091     return layout;
34092 };
34093
34094 // private
34095 Roo.BorderLayout.RegionFactory = {
34096     // private
34097     validRegions : ["north","south","east","west","center"],
34098
34099     // private
34100     create : function(target, mgr, config){
34101         target = target.toLowerCase();
34102         if(config.lightweight || config.basic){
34103             return new Roo.BasicLayoutRegion(mgr, config, target);
34104         }
34105         switch(target){
34106             case "north":
34107                 return new Roo.NorthLayoutRegion(mgr, config);
34108             case "south":
34109                 return new Roo.SouthLayoutRegion(mgr, config);
34110             case "east":
34111                 return new Roo.EastLayoutRegion(mgr, config);
34112             case "west":
34113                 return new Roo.WestLayoutRegion(mgr, config);
34114             case "center":
34115                 return new Roo.CenterLayoutRegion(mgr, config);
34116         }
34117         throw 'Layout region "'+target+'" not supported.';
34118     }
34119 };/*
34120  * Based on:
34121  * Ext JS Library 1.1.1
34122  * Copyright(c) 2006-2007, Ext JS, LLC.
34123  *
34124  * Originally Released Under LGPL - original licence link has changed is not relivant.
34125  *
34126  * Fork - LGPL
34127  * <script type="text/javascript">
34128  */
34129  
34130 /**
34131  * @class Roo.BasicLayoutRegion
34132  * @extends Roo.util.Observable
34133  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34134  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34135  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34136  */
34137 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34138     this.mgr = mgr;
34139     this.position  = pos;
34140     this.events = {
34141         /**
34142          * @scope Roo.BasicLayoutRegion
34143          */
34144         
34145         /**
34146          * @event beforeremove
34147          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34148          * @param {Roo.LayoutRegion} this
34149          * @param {Roo.ContentPanel} panel The panel
34150          * @param {Object} e The cancel event object
34151          */
34152         "beforeremove" : true,
34153         /**
34154          * @event invalidated
34155          * Fires when the layout for this region is changed.
34156          * @param {Roo.LayoutRegion} this
34157          */
34158         "invalidated" : true,
34159         /**
34160          * @event visibilitychange
34161          * Fires when this region is shown or hidden 
34162          * @param {Roo.LayoutRegion} this
34163          * @param {Boolean} visibility true or false
34164          */
34165         "visibilitychange" : true,
34166         /**
34167          * @event paneladded
34168          * Fires when a panel is added. 
34169          * @param {Roo.LayoutRegion} this
34170          * @param {Roo.ContentPanel} panel The panel
34171          */
34172         "paneladded" : true,
34173         /**
34174          * @event panelremoved
34175          * Fires when a panel is removed. 
34176          * @param {Roo.LayoutRegion} this
34177          * @param {Roo.ContentPanel} panel The panel
34178          */
34179         "panelremoved" : true,
34180         /**
34181          * @event beforecollapse
34182          * Fires when this region before collapse.
34183          * @param {Roo.LayoutRegion} this
34184          */
34185         "beforecollapse" : true,
34186         /**
34187          * @event collapsed
34188          * Fires when this region is collapsed.
34189          * @param {Roo.LayoutRegion} this
34190          */
34191         "collapsed" : true,
34192         /**
34193          * @event expanded
34194          * Fires when this region is expanded.
34195          * @param {Roo.LayoutRegion} this
34196          */
34197         "expanded" : true,
34198         /**
34199          * @event slideshow
34200          * Fires when this region is slid into view.
34201          * @param {Roo.LayoutRegion} this
34202          */
34203         "slideshow" : true,
34204         /**
34205          * @event slidehide
34206          * Fires when this region slides out of view. 
34207          * @param {Roo.LayoutRegion} this
34208          */
34209         "slidehide" : true,
34210         /**
34211          * @event panelactivated
34212          * Fires when a panel is activated. 
34213          * @param {Roo.LayoutRegion} this
34214          * @param {Roo.ContentPanel} panel The activated panel
34215          */
34216         "panelactivated" : true,
34217         /**
34218          * @event resized
34219          * Fires when the user resizes this region. 
34220          * @param {Roo.LayoutRegion} this
34221          * @param {Number} newSize The new size (width for east/west, height for north/south)
34222          */
34223         "resized" : true
34224     };
34225     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34226     this.panels = new Roo.util.MixedCollection();
34227     this.panels.getKey = this.getPanelId.createDelegate(this);
34228     this.box = null;
34229     this.activePanel = null;
34230     // ensure listeners are added...
34231     
34232     if (config.listeners || config.events) {
34233         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34234             listeners : config.listeners || {},
34235             events : config.events || {}
34236         });
34237     }
34238     
34239     if(skipConfig !== true){
34240         this.applyConfig(config);
34241     }
34242 };
34243
34244 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34245     getPanelId : function(p){
34246         return p.getId();
34247     },
34248     
34249     applyConfig : function(config){
34250         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34251         this.config = config;
34252         
34253     },
34254     
34255     /**
34256      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34257      * the width, for horizontal (north, south) the height.
34258      * @param {Number} newSize The new width or height
34259      */
34260     resizeTo : function(newSize){
34261         var el = this.el ? this.el :
34262                  (this.activePanel ? this.activePanel.getEl() : null);
34263         if(el){
34264             switch(this.position){
34265                 case "east":
34266                 case "west":
34267                     el.setWidth(newSize);
34268                     this.fireEvent("resized", this, newSize);
34269                 break;
34270                 case "north":
34271                 case "south":
34272                     el.setHeight(newSize);
34273                     this.fireEvent("resized", this, newSize);
34274                 break;                
34275             }
34276         }
34277     },
34278     
34279     getBox : function(){
34280         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34281     },
34282     
34283     getMargins : function(){
34284         return this.margins;
34285     },
34286     
34287     updateBox : function(box){
34288         this.box = box;
34289         var el = this.activePanel.getEl();
34290         el.dom.style.left = box.x + "px";
34291         el.dom.style.top = box.y + "px";
34292         this.activePanel.setSize(box.width, box.height);
34293     },
34294     
34295     /**
34296      * Returns the container element for this region.
34297      * @return {Roo.Element}
34298      */
34299     getEl : function(){
34300         return this.activePanel;
34301     },
34302     
34303     /**
34304      * Returns true if this region is currently visible.
34305      * @return {Boolean}
34306      */
34307     isVisible : function(){
34308         return this.activePanel ? true : false;
34309     },
34310     
34311     setActivePanel : function(panel){
34312         panel = this.getPanel(panel);
34313         if(this.activePanel && this.activePanel != panel){
34314             this.activePanel.setActiveState(false);
34315             this.activePanel.getEl().setLeftTop(-10000,-10000);
34316         }
34317         this.activePanel = panel;
34318         panel.setActiveState(true);
34319         if(this.box){
34320             panel.setSize(this.box.width, this.box.height);
34321         }
34322         this.fireEvent("panelactivated", this, panel);
34323         this.fireEvent("invalidated");
34324     },
34325     
34326     /**
34327      * Show the specified panel.
34328      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34329      * @return {Roo.ContentPanel} The shown panel or null
34330      */
34331     showPanel : function(panel){
34332         if(panel = this.getPanel(panel)){
34333             this.setActivePanel(panel);
34334         }
34335         return panel;
34336     },
34337     
34338     /**
34339      * Get the active panel for this region.
34340      * @return {Roo.ContentPanel} The active panel or null
34341      */
34342     getActivePanel : function(){
34343         return this.activePanel;
34344     },
34345     
34346     /**
34347      * Add the passed ContentPanel(s)
34348      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34349      * @return {Roo.ContentPanel} The panel added (if only one was added)
34350      */
34351     add : function(panel){
34352         if(arguments.length > 1){
34353             for(var i = 0, len = arguments.length; i < len; i++) {
34354                 this.add(arguments[i]);
34355             }
34356             return null;
34357         }
34358         if(this.hasPanel(panel)){
34359             this.showPanel(panel);
34360             return panel;
34361         }
34362         var el = panel.getEl();
34363         if(el.dom.parentNode != this.mgr.el.dom){
34364             this.mgr.el.dom.appendChild(el.dom);
34365         }
34366         if(panel.setRegion){
34367             panel.setRegion(this);
34368         }
34369         this.panels.add(panel);
34370         el.setStyle("position", "absolute");
34371         if(!panel.background){
34372             this.setActivePanel(panel);
34373             if(this.config.initialSize && this.panels.getCount()==1){
34374                 this.resizeTo(this.config.initialSize);
34375             }
34376         }
34377         this.fireEvent("paneladded", this, panel);
34378         return panel;
34379     },
34380     
34381     /**
34382      * Returns true if the panel is in this region.
34383      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34384      * @return {Boolean}
34385      */
34386     hasPanel : function(panel){
34387         if(typeof panel == "object"){ // must be panel obj
34388             panel = panel.getId();
34389         }
34390         return this.getPanel(panel) ? true : false;
34391     },
34392     
34393     /**
34394      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34395      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34396      * @param {Boolean} preservePanel Overrides the config preservePanel option
34397      * @return {Roo.ContentPanel} The panel that was removed
34398      */
34399     remove : function(panel, preservePanel){
34400         panel = this.getPanel(panel);
34401         if(!panel){
34402             return null;
34403         }
34404         var e = {};
34405         this.fireEvent("beforeremove", this, panel, e);
34406         if(e.cancel === true){
34407             return null;
34408         }
34409         var panelId = panel.getId();
34410         this.panels.removeKey(panelId);
34411         return panel;
34412     },
34413     
34414     /**
34415      * Returns the panel specified or null if it's not in this region.
34416      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34417      * @return {Roo.ContentPanel}
34418      */
34419     getPanel : function(id){
34420         if(typeof id == "object"){ // must be panel obj
34421             return id;
34422         }
34423         return this.panels.get(id);
34424     },
34425     
34426     /**
34427      * Returns this regions position (north/south/east/west/center).
34428      * @return {String} 
34429      */
34430     getPosition: function(){
34431         return this.position;    
34432     }
34433 });/*
34434  * Based on:
34435  * Ext JS Library 1.1.1
34436  * Copyright(c) 2006-2007, Ext JS, LLC.
34437  *
34438  * Originally Released Under LGPL - original licence link has changed is not relivant.
34439  *
34440  * Fork - LGPL
34441  * <script type="text/javascript">
34442  */
34443  
34444 /**
34445  * @class Roo.LayoutRegion
34446  * @extends Roo.BasicLayoutRegion
34447  * This class represents a region in a layout manager.
34448  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
34449  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
34450  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
34451  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34452  * @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})
34453  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34454  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
34455  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34456  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34457  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34458  * @cfg {String}    title           The title for the region (overrides panel titles)
34459  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34460  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34461  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34462  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34463  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34464  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34465  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34466  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34467  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34468  * @cfg {Boolean}   showPin         True to show a pin button
34469  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34470  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34471  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34472  * @cfg {Number}    width           For East/West panels
34473  * @cfg {Number}    height          For North/South panels
34474  * @cfg {Boolean}   split           To show the splitter
34475  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34476  */
34477 Roo.LayoutRegion = function(mgr, config, pos){
34478     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34479     var dh = Roo.DomHelper;
34480     /** This region's container element 
34481     * @type Roo.Element */
34482     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34483     /** This region's title element 
34484     * @type Roo.Element */
34485
34486     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34487         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34488         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34489     ]}, true);
34490     this.titleEl.enableDisplayMode();
34491     /** This region's title text element 
34492     * @type HTMLElement */
34493     this.titleTextEl = this.titleEl.dom.firstChild;
34494     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34495     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34496     this.closeBtn.enableDisplayMode();
34497     this.closeBtn.on("click", this.closeClicked, this);
34498     this.closeBtn.hide();
34499
34500     this.createBody(config);
34501     this.visible = true;
34502     this.collapsed = false;
34503
34504     if(config.hideWhenEmpty){
34505         this.hide();
34506         this.on("paneladded", this.validateVisibility, this);
34507         this.on("panelremoved", this.validateVisibility, this);
34508     }
34509     this.applyConfig(config);
34510 };
34511
34512 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34513
34514     createBody : function(){
34515         /** This region's body element 
34516         * @type Roo.Element */
34517         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34518     },
34519
34520     applyConfig : function(c){
34521         if(c.collapsible && this.position != "center" && !this.collapsedEl){
34522             var dh = Roo.DomHelper;
34523             if(c.titlebar !== false){
34524                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34525                 this.collapseBtn.on("click", this.collapse, this);
34526                 this.collapseBtn.enableDisplayMode();
34527
34528                 if(c.showPin === true || this.showPin){
34529                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34530                     this.stickBtn.enableDisplayMode();
34531                     this.stickBtn.on("click", this.expand, this);
34532                     this.stickBtn.hide();
34533                 }
34534             }
34535             /** This region's collapsed element
34536             * @type Roo.Element */
34537             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34538                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34539             ]}, true);
34540             if(c.floatable !== false){
34541                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34542                this.collapsedEl.on("click", this.collapseClick, this);
34543             }
34544
34545             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34546                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34547                    id: "message", unselectable: "on", style:{"float":"left"}});
34548                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34549              }
34550             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34551             this.expandBtn.on("click", this.expand, this);
34552         }
34553         if(this.collapseBtn){
34554             this.collapseBtn.setVisible(c.collapsible == true);
34555         }
34556         this.cmargins = c.cmargins || this.cmargins ||
34557                          (this.position == "west" || this.position == "east" ?
34558                              {top: 0, left: 2, right:2, bottom: 0} :
34559                              {top: 2, left: 0, right:0, bottom: 2});
34560         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34561         this.bottomTabs = c.tabPosition != "top";
34562         this.autoScroll = c.autoScroll || false;
34563         if(this.autoScroll){
34564             this.bodyEl.setStyle("overflow", "auto");
34565         }else{
34566             this.bodyEl.setStyle("overflow", "hidden");
34567         }
34568         //if(c.titlebar !== false){
34569             if((!c.titlebar && !c.title) || c.titlebar === false){
34570                 this.titleEl.hide();
34571             }else{
34572                 this.titleEl.show();
34573                 if(c.title){
34574                     this.titleTextEl.innerHTML = c.title;
34575                 }
34576             }
34577         //}
34578         this.duration = c.duration || .30;
34579         this.slideDuration = c.slideDuration || .45;
34580         this.config = c;
34581         if(c.collapsed){
34582             this.collapse(true);
34583         }
34584         if(c.hidden){
34585             this.hide();
34586         }
34587     },
34588     /**
34589      * Returns true if this region is currently visible.
34590      * @return {Boolean}
34591      */
34592     isVisible : function(){
34593         return this.visible;
34594     },
34595
34596     /**
34597      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34598      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34599      */
34600     setCollapsedTitle : function(title){
34601         title = title || "&#160;";
34602         if(this.collapsedTitleTextEl){
34603             this.collapsedTitleTextEl.innerHTML = title;
34604         }
34605     },
34606
34607     getBox : function(){
34608         var b;
34609         if(!this.collapsed){
34610             b = this.el.getBox(false, true);
34611         }else{
34612             b = this.collapsedEl.getBox(false, true);
34613         }
34614         return b;
34615     },
34616
34617     getMargins : function(){
34618         return this.collapsed ? this.cmargins : this.margins;
34619     },
34620
34621     highlight : function(){
34622         this.el.addClass("x-layout-panel-dragover");
34623     },
34624
34625     unhighlight : function(){
34626         this.el.removeClass("x-layout-panel-dragover");
34627     },
34628
34629     updateBox : function(box){
34630         this.box = box;
34631         if(!this.collapsed){
34632             this.el.dom.style.left = box.x + "px";
34633             this.el.dom.style.top = box.y + "px";
34634             this.updateBody(box.width, box.height);
34635         }else{
34636             this.collapsedEl.dom.style.left = box.x + "px";
34637             this.collapsedEl.dom.style.top = box.y + "px";
34638             this.collapsedEl.setSize(box.width, box.height);
34639         }
34640         if(this.tabs){
34641             this.tabs.autoSizeTabs();
34642         }
34643     },
34644
34645     updateBody : function(w, h){
34646         if(w !== null){
34647             this.el.setWidth(w);
34648             w -= this.el.getBorderWidth("rl");
34649             if(this.config.adjustments){
34650                 w += this.config.adjustments[0];
34651             }
34652         }
34653         if(h !== null){
34654             this.el.setHeight(h);
34655             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34656             h -= this.el.getBorderWidth("tb");
34657             if(this.config.adjustments){
34658                 h += this.config.adjustments[1];
34659             }
34660             this.bodyEl.setHeight(h);
34661             if(this.tabs){
34662                 h = this.tabs.syncHeight(h);
34663             }
34664         }
34665         if(this.panelSize){
34666             w = w !== null ? w : this.panelSize.width;
34667             h = h !== null ? h : this.panelSize.height;
34668         }
34669         if(this.activePanel){
34670             var el = this.activePanel.getEl();
34671             w = w !== null ? w : el.getWidth();
34672             h = h !== null ? h : el.getHeight();
34673             this.panelSize = {width: w, height: h};
34674             this.activePanel.setSize(w, h);
34675         }
34676         if(Roo.isIE && this.tabs){
34677             this.tabs.el.repaint();
34678         }
34679     },
34680
34681     /**
34682      * Returns the container element for this region.
34683      * @return {Roo.Element}
34684      */
34685     getEl : function(){
34686         return this.el;
34687     },
34688
34689     /**
34690      * Hides this region.
34691      */
34692     hide : function(){
34693         if(!this.collapsed){
34694             this.el.dom.style.left = "-2000px";
34695             this.el.hide();
34696         }else{
34697             this.collapsedEl.dom.style.left = "-2000px";
34698             this.collapsedEl.hide();
34699         }
34700         this.visible = false;
34701         this.fireEvent("visibilitychange", this, false);
34702     },
34703
34704     /**
34705      * Shows this region if it was previously hidden.
34706      */
34707     show : function(){
34708         if(!this.collapsed){
34709             this.el.show();
34710         }else{
34711             this.collapsedEl.show();
34712         }
34713         this.visible = true;
34714         this.fireEvent("visibilitychange", this, true);
34715     },
34716
34717     closeClicked : function(){
34718         if(this.activePanel){
34719             this.remove(this.activePanel);
34720         }
34721     },
34722
34723     collapseClick : function(e){
34724         if(this.isSlid){
34725            e.stopPropagation();
34726            this.slideIn();
34727         }else{
34728            e.stopPropagation();
34729            this.slideOut();
34730         }
34731     },
34732
34733     /**
34734      * Collapses this region.
34735      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34736      */
34737     collapse : function(skipAnim, skipCheck){
34738         if(this.collapsed) {
34739             return;
34740         }
34741         
34742         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34743             
34744             this.collapsed = true;
34745             if(this.split){
34746                 this.split.el.hide();
34747             }
34748             if(this.config.animate && skipAnim !== true){
34749                 this.fireEvent("invalidated", this);
34750                 this.animateCollapse();
34751             }else{
34752                 this.el.setLocation(-20000,-20000);
34753                 this.el.hide();
34754                 this.collapsedEl.show();
34755                 this.fireEvent("collapsed", this);
34756                 this.fireEvent("invalidated", this);
34757             }
34758         }
34759         
34760     },
34761
34762     animateCollapse : function(){
34763         // overridden
34764     },
34765
34766     /**
34767      * Expands this region if it was previously collapsed.
34768      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34769      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34770      */
34771     expand : function(e, skipAnim){
34772         if(e) {
34773             e.stopPropagation();
34774         }
34775         if(!this.collapsed || this.el.hasActiveFx()) {
34776             return;
34777         }
34778         if(this.isSlid){
34779             this.afterSlideIn();
34780             skipAnim = true;
34781         }
34782         this.collapsed = false;
34783         if(this.config.animate && skipAnim !== true){
34784             this.animateExpand();
34785         }else{
34786             this.el.show();
34787             if(this.split){
34788                 this.split.el.show();
34789             }
34790             this.collapsedEl.setLocation(-2000,-2000);
34791             this.collapsedEl.hide();
34792             this.fireEvent("invalidated", this);
34793             this.fireEvent("expanded", this);
34794         }
34795     },
34796
34797     animateExpand : function(){
34798         // overridden
34799     },
34800
34801     initTabs : function()
34802     {
34803         this.bodyEl.setStyle("overflow", "hidden");
34804         var ts = new Roo.TabPanel(
34805                 this.bodyEl.dom,
34806                 {
34807                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34808                     disableTooltips: this.config.disableTabTips,
34809                     toolbar : this.config.toolbar
34810                 }
34811         );
34812         if(this.config.hideTabs){
34813             ts.stripWrap.setDisplayed(false);
34814         }
34815         this.tabs = ts;
34816         ts.resizeTabs = this.config.resizeTabs === true;
34817         ts.minTabWidth = this.config.minTabWidth || 40;
34818         ts.maxTabWidth = this.config.maxTabWidth || 250;
34819         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34820         ts.monitorResize = false;
34821         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34822         ts.bodyEl.addClass('x-layout-tabs-body');
34823         this.panels.each(this.initPanelAsTab, this);
34824     },
34825
34826     initPanelAsTab : function(panel){
34827         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34828                     this.config.closeOnTab && panel.isClosable());
34829         if(panel.tabTip !== undefined){
34830             ti.setTooltip(panel.tabTip);
34831         }
34832         ti.on("activate", function(){
34833               this.setActivePanel(panel);
34834         }, this);
34835         if(this.config.closeOnTab){
34836             ti.on("beforeclose", function(t, e){
34837                 e.cancel = true;
34838                 this.remove(panel);
34839             }, this);
34840         }
34841         return ti;
34842     },
34843
34844     updatePanelTitle : function(panel, title){
34845         if(this.activePanel == panel){
34846             this.updateTitle(title);
34847         }
34848         if(this.tabs){
34849             var ti = this.tabs.getTab(panel.getEl().id);
34850             ti.setText(title);
34851             if(panel.tabTip !== undefined){
34852                 ti.setTooltip(panel.tabTip);
34853             }
34854         }
34855     },
34856
34857     updateTitle : function(title){
34858         if(this.titleTextEl && !this.config.title){
34859             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34860         }
34861     },
34862
34863     setActivePanel : function(panel){
34864         panel = this.getPanel(panel);
34865         if(this.activePanel && this.activePanel != panel){
34866             this.activePanel.setActiveState(false);
34867         }
34868         this.activePanel = panel;
34869         panel.setActiveState(true);
34870         if(this.panelSize){
34871             panel.setSize(this.panelSize.width, this.panelSize.height);
34872         }
34873         if(this.closeBtn){
34874             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34875         }
34876         this.updateTitle(panel.getTitle());
34877         if(this.tabs){
34878             this.fireEvent("invalidated", this);
34879         }
34880         this.fireEvent("panelactivated", this, panel);
34881     },
34882
34883     /**
34884      * Shows the specified panel.
34885      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34886      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34887      */
34888     showPanel : function(panel)
34889     {
34890         panel = this.getPanel(panel);
34891         if(panel){
34892             if(this.tabs){
34893                 var tab = this.tabs.getTab(panel.getEl().id);
34894                 if(tab.isHidden()){
34895                     this.tabs.unhideTab(tab.id);
34896                 }
34897                 tab.activate();
34898             }else{
34899                 this.setActivePanel(panel);
34900             }
34901         }
34902         return panel;
34903     },
34904
34905     /**
34906      * Get the active panel for this region.
34907      * @return {Roo.ContentPanel} The active panel or null
34908      */
34909     getActivePanel : function(){
34910         return this.activePanel;
34911     },
34912
34913     validateVisibility : function(){
34914         if(this.panels.getCount() < 1){
34915             this.updateTitle("&#160;");
34916             this.closeBtn.hide();
34917             this.hide();
34918         }else{
34919             if(!this.isVisible()){
34920                 this.show();
34921             }
34922         }
34923     },
34924
34925     /**
34926      * Adds the passed ContentPanel(s) to this region.
34927      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34928      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34929      */
34930     add : function(panel){
34931         if(arguments.length > 1){
34932             for(var i = 0, len = arguments.length; i < len; i++) {
34933                 this.add(arguments[i]);
34934             }
34935             return null;
34936         }
34937         if(this.hasPanel(panel)){
34938             this.showPanel(panel);
34939             return panel;
34940         }
34941         panel.setRegion(this);
34942         this.panels.add(panel);
34943         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34944             this.bodyEl.dom.appendChild(panel.getEl().dom);
34945             if(panel.background !== true){
34946                 this.setActivePanel(panel);
34947             }
34948             this.fireEvent("paneladded", this, panel);
34949             return panel;
34950         }
34951         if(!this.tabs){
34952             this.initTabs();
34953         }else{
34954             this.initPanelAsTab(panel);
34955         }
34956         if(panel.background !== true){
34957             this.tabs.activate(panel.getEl().id);
34958         }
34959         this.fireEvent("paneladded", this, panel);
34960         return panel;
34961     },
34962
34963     /**
34964      * Hides the tab for the specified panel.
34965      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34966      */
34967     hidePanel : function(panel){
34968         if(this.tabs && (panel = this.getPanel(panel))){
34969             this.tabs.hideTab(panel.getEl().id);
34970         }
34971     },
34972
34973     /**
34974      * Unhides the tab for a previously hidden panel.
34975      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34976      */
34977     unhidePanel : function(panel){
34978         if(this.tabs && (panel = this.getPanel(panel))){
34979             this.tabs.unhideTab(panel.getEl().id);
34980         }
34981     },
34982
34983     clearPanels : function(){
34984         while(this.panels.getCount() > 0){
34985              this.remove(this.panels.first());
34986         }
34987     },
34988
34989     /**
34990      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34991      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34992      * @param {Boolean} preservePanel Overrides the config preservePanel option
34993      * @return {Roo.ContentPanel} The panel that was removed
34994      */
34995     remove : function(panel, preservePanel){
34996         panel = this.getPanel(panel);
34997         if(!panel){
34998             return null;
34999         }
35000         var e = {};
35001         this.fireEvent("beforeremove", this, panel, e);
35002         if(e.cancel === true){
35003             return null;
35004         }
35005         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35006         var panelId = panel.getId();
35007         this.panels.removeKey(panelId);
35008         if(preservePanel){
35009             document.body.appendChild(panel.getEl().dom);
35010         }
35011         if(this.tabs){
35012             this.tabs.removeTab(panel.getEl().id);
35013         }else if (!preservePanel){
35014             this.bodyEl.dom.removeChild(panel.getEl().dom);
35015         }
35016         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35017             var p = this.panels.first();
35018             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35019             tempEl.appendChild(p.getEl().dom);
35020             this.bodyEl.update("");
35021             this.bodyEl.dom.appendChild(p.getEl().dom);
35022             tempEl = null;
35023             this.updateTitle(p.getTitle());
35024             this.tabs = null;
35025             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35026             this.setActivePanel(p);
35027         }
35028         panel.setRegion(null);
35029         if(this.activePanel == panel){
35030             this.activePanel = null;
35031         }
35032         if(this.config.autoDestroy !== false && preservePanel !== true){
35033             try{panel.destroy();}catch(e){}
35034         }
35035         this.fireEvent("panelremoved", this, panel);
35036         return panel;
35037     },
35038
35039     /**
35040      * Returns the TabPanel component used by this region
35041      * @return {Roo.TabPanel}
35042      */
35043     getTabs : function(){
35044         return this.tabs;
35045     },
35046
35047     createTool : function(parentEl, className){
35048         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35049             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
35050         btn.addClassOnOver("x-layout-tools-button-over");
35051         return btn;
35052     }
35053 });/*
35054  * Based on:
35055  * Ext JS Library 1.1.1
35056  * Copyright(c) 2006-2007, Ext JS, LLC.
35057  *
35058  * Originally Released Under LGPL - original licence link has changed is not relivant.
35059  *
35060  * Fork - LGPL
35061  * <script type="text/javascript">
35062  */
35063  
35064
35065
35066 /**
35067  * @class Roo.SplitLayoutRegion
35068  * @extends Roo.LayoutRegion
35069  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35070  */
35071 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35072     this.cursor = cursor;
35073     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35074 };
35075
35076 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35077     splitTip : "Drag to resize.",
35078     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35079     useSplitTips : false,
35080
35081     applyConfig : function(config){
35082         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35083         if(config.split){
35084             if(!this.split){
35085                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
35086                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
35087                 /** The SplitBar for this region 
35088                 * @type Roo.SplitBar */
35089                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35090                 this.split.on("moved", this.onSplitMove, this);
35091                 this.split.useShim = config.useShim === true;
35092                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35093                 if(this.useSplitTips){
35094                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35095                 }
35096                 if(config.collapsible){
35097                     this.split.el.on("dblclick", this.collapse,  this);
35098                 }
35099             }
35100             if(typeof config.minSize != "undefined"){
35101                 this.split.minSize = config.minSize;
35102             }
35103             if(typeof config.maxSize != "undefined"){
35104                 this.split.maxSize = config.maxSize;
35105             }
35106             if(config.hideWhenEmpty || config.hidden || config.collapsed){
35107                 this.hideSplitter();
35108             }
35109         }
35110     },
35111
35112     getHMaxSize : function(){
35113          var cmax = this.config.maxSize || 10000;
35114          var center = this.mgr.getRegion("center");
35115          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35116     },
35117
35118     getVMaxSize : function(){
35119          var cmax = this.config.maxSize || 10000;
35120          var center = this.mgr.getRegion("center");
35121          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35122     },
35123
35124     onSplitMove : function(split, newSize){
35125         this.fireEvent("resized", this, newSize);
35126     },
35127     
35128     /** 
35129      * Returns the {@link Roo.SplitBar} for this region.
35130      * @return {Roo.SplitBar}
35131      */
35132     getSplitBar : function(){
35133         return this.split;
35134     },
35135     
35136     hide : function(){
35137         this.hideSplitter();
35138         Roo.SplitLayoutRegion.superclass.hide.call(this);
35139     },
35140
35141     hideSplitter : function(){
35142         if(this.split){
35143             this.split.el.setLocation(-2000,-2000);
35144             this.split.el.hide();
35145         }
35146     },
35147
35148     show : function(){
35149         if(this.split){
35150             this.split.el.show();
35151         }
35152         Roo.SplitLayoutRegion.superclass.show.call(this);
35153     },
35154     
35155     beforeSlide: function(){
35156         if(Roo.isGecko){// firefox overflow auto bug workaround
35157             this.bodyEl.clip();
35158             if(this.tabs) {
35159                 this.tabs.bodyEl.clip();
35160             }
35161             if(this.activePanel){
35162                 this.activePanel.getEl().clip();
35163                 
35164                 if(this.activePanel.beforeSlide){
35165                     this.activePanel.beforeSlide();
35166                 }
35167             }
35168         }
35169     },
35170     
35171     afterSlide : function(){
35172         if(Roo.isGecko){// firefox overflow auto bug workaround
35173             this.bodyEl.unclip();
35174             if(this.tabs) {
35175                 this.tabs.bodyEl.unclip();
35176             }
35177             if(this.activePanel){
35178                 this.activePanel.getEl().unclip();
35179                 if(this.activePanel.afterSlide){
35180                     this.activePanel.afterSlide();
35181                 }
35182             }
35183         }
35184     },
35185
35186     initAutoHide : function(){
35187         if(this.autoHide !== false){
35188             if(!this.autoHideHd){
35189                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35190                 this.autoHideHd = {
35191                     "mouseout": function(e){
35192                         if(!e.within(this.el, true)){
35193                             st.delay(500);
35194                         }
35195                     },
35196                     "mouseover" : function(e){
35197                         st.cancel();
35198                     },
35199                     scope : this
35200                 };
35201             }
35202             this.el.on(this.autoHideHd);
35203         }
35204     },
35205
35206     clearAutoHide : function(){
35207         if(this.autoHide !== false){
35208             this.el.un("mouseout", this.autoHideHd.mouseout);
35209             this.el.un("mouseover", this.autoHideHd.mouseover);
35210         }
35211     },
35212
35213     clearMonitor : function(){
35214         Roo.get(document).un("click", this.slideInIf, this);
35215     },
35216
35217     // these names are backwards but not changed for compat
35218     slideOut : function(){
35219         if(this.isSlid || this.el.hasActiveFx()){
35220             return;
35221         }
35222         this.isSlid = true;
35223         if(this.collapseBtn){
35224             this.collapseBtn.hide();
35225         }
35226         this.closeBtnState = this.closeBtn.getStyle('display');
35227         this.closeBtn.hide();
35228         if(this.stickBtn){
35229             this.stickBtn.show();
35230         }
35231         this.el.show();
35232         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35233         this.beforeSlide();
35234         this.el.setStyle("z-index", 10001);
35235         this.el.slideIn(this.getSlideAnchor(), {
35236             callback: function(){
35237                 this.afterSlide();
35238                 this.initAutoHide();
35239                 Roo.get(document).on("click", this.slideInIf, this);
35240                 this.fireEvent("slideshow", this);
35241             },
35242             scope: this,
35243             block: true
35244         });
35245     },
35246
35247     afterSlideIn : function(){
35248         this.clearAutoHide();
35249         this.isSlid = false;
35250         this.clearMonitor();
35251         this.el.setStyle("z-index", "");
35252         if(this.collapseBtn){
35253             this.collapseBtn.show();
35254         }
35255         this.closeBtn.setStyle('display', this.closeBtnState);
35256         if(this.stickBtn){
35257             this.stickBtn.hide();
35258         }
35259         this.fireEvent("slidehide", this);
35260     },
35261
35262     slideIn : function(cb){
35263         if(!this.isSlid || this.el.hasActiveFx()){
35264             Roo.callback(cb);
35265             return;
35266         }
35267         this.isSlid = false;
35268         this.beforeSlide();
35269         this.el.slideOut(this.getSlideAnchor(), {
35270             callback: function(){
35271                 this.el.setLeftTop(-10000, -10000);
35272                 this.afterSlide();
35273                 this.afterSlideIn();
35274                 Roo.callback(cb);
35275             },
35276             scope: this,
35277             block: true
35278         });
35279     },
35280     
35281     slideInIf : function(e){
35282         if(!e.within(this.el)){
35283             this.slideIn();
35284         }
35285     },
35286
35287     animateCollapse : function(){
35288         this.beforeSlide();
35289         this.el.setStyle("z-index", 20000);
35290         var anchor = this.getSlideAnchor();
35291         this.el.slideOut(anchor, {
35292             callback : function(){
35293                 this.el.setStyle("z-index", "");
35294                 this.collapsedEl.slideIn(anchor, {duration:.3});
35295                 this.afterSlide();
35296                 this.el.setLocation(-10000,-10000);
35297                 this.el.hide();
35298                 this.fireEvent("collapsed", this);
35299             },
35300             scope: this,
35301             block: true
35302         });
35303     },
35304
35305     animateExpand : function(){
35306         this.beforeSlide();
35307         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35308         this.el.setStyle("z-index", 20000);
35309         this.collapsedEl.hide({
35310             duration:.1
35311         });
35312         this.el.slideIn(this.getSlideAnchor(), {
35313             callback : function(){
35314                 this.el.setStyle("z-index", "");
35315                 this.afterSlide();
35316                 if(this.split){
35317                     this.split.el.show();
35318                 }
35319                 this.fireEvent("invalidated", this);
35320                 this.fireEvent("expanded", this);
35321             },
35322             scope: this,
35323             block: true
35324         });
35325     },
35326
35327     anchors : {
35328         "west" : "left",
35329         "east" : "right",
35330         "north" : "top",
35331         "south" : "bottom"
35332     },
35333
35334     sanchors : {
35335         "west" : "l",
35336         "east" : "r",
35337         "north" : "t",
35338         "south" : "b"
35339     },
35340
35341     canchors : {
35342         "west" : "tl-tr",
35343         "east" : "tr-tl",
35344         "north" : "tl-bl",
35345         "south" : "bl-tl"
35346     },
35347
35348     getAnchor : function(){
35349         return this.anchors[this.position];
35350     },
35351
35352     getCollapseAnchor : function(){
35353         return this.canchors[this.position];
35354     },
35355
35356     getSlideAnchor : function(){
35357         return this.sanchors[this.position];
35358     },
35359
35360     getAlignAdj : function(){
35361         var cm = this.cmargins;
35362         switch(this.position){
35363             case "west":
35364                 return [0, 0];
35365             break;
35366             case "east":
35367                 return [0, 0];
35368             break;
35369             case "north":
35370                 return [0, 0];
35371             break;
35372             case "south":
35373                 return [0, 0];
35374             break;
35375         }
35376     },
35377
35378     getExpandAdj : function(){
35379         var c = this.collapsedEl, cm = this.cmargins;
35380         switch(this.position){
35381             case "west":
35382                 return [-(cm.right+c.getWidth()+cm.left), 0];
35383             break;
35384             case "east":
35385                 return [cm.right+c.getWidth()+cm.left, 0];
35386             break;
35387             case "north":
35388                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35389             break;
35390             case "south":
35391                 return [0, cm.top+cm.bottom+c.getHeight()];
35392             break;
35393         }
35394     }
35395 });/*
35396  * Based on:
35397  * Ext JS Library 1.1.1
35398  * Copyright(c) 2006-2007, Ext JS, LLC.
35399  *
35400  * Originally Released Under LGPL - original licence link has changed is not relivant.
35401  *
35402  * Fork - LGPL
35403  * <script type="text/javascript">
35404  */
35405 /*
35406  * These classes are private internal classes
35407  */
35408 Roo.CenterLayoutRegion = function(mgr, config){
35409     Roo.LayoutRegion.call(this, mgr, config, "center");
35410     this.visible = true;
35411     this.minWidth = config.minWidth || 20;
35412     this.minHeight = config.minHeight || 20;
35413 };
35414
35415 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35416     hide : function(){
35417         // center panel can't be hidden
35418     },
35419     
35420     show : function(){
35421         // center panel can't be hidden
35422     },
35423     
35424     getMinWidth: function(){
35425         return this.minWidth;
35426     },
35427     
35428     getMinHeight: function(){
35429         return this.minHeight;
35430     }
35431 });
35432
35433
35434 Roo.NorthLayoutRegion = function(mgr, config){
35435     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35436     if(this.split){
35437         this.split.placement = Roo.SplitBar.TOP;
35438         this.split.orientation = Roo.SplitBar.VERTICAL;
35439         this.split.el.addClass("x-layout-split-v");
35440     }
35441     var size = config.initialSize || config.height;
35442     if(typeof size != "undefined"){
35443         this.el.setHeight(size);
35444     }
35445 };
35446 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35447     orientation: Roo.SplitBar.VERTICAL,
35448     getBox : function(){
35449         if(this.collapsed){
35450             return this.collapsedEl.getBox();
35451         }
35452         var box = this.el.getBox();
35453         if(this.split){
35454             box.height += this.split.el.getHeight();
35455         }
35456         return box;
35457     },
35458     
35459     updateBox : function(box){
35460         if(this.split && !this.collapsed){
35461             box.height -= this.split.el.getHeight();
35462             this.split.el.setLeft(box.x);
35463             this.split.el.setTop(box.y+box.height);
35464             this.split.el.setWidth(box.width);
35465         }
35466         if(this.collapsed){
35467             this.updateBody(box.width, null);
35468         }
35469         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35470     }
35471 });
35472
35473 Roo.SouthLayoutRegion = function(mgr, config){
35474     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35475     if(this.split){
35476         this.split.placement = Roo.SplitBar.BOTTOM;
35477         this.split.orientation = Roo.SplitBar.VERTICAL;
35478         this.split.el.addClass("x-layout-split-v");
35479     }
35480     var size = config.initialSize || config.height;
35481     if(typeof size != "undefined"){
35482         this.el.setHeight(size);
35483     }
35484 };
35485 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35486     orientation: Roo.SplitBar.VERTICAL,
35487     getBox : function(){
35488         if(this.collapsed){
35489             return this.collapsedEl.getBox();
35490         }
35491         var box = this.el.getBox();
35492         if(this.split){
35493             var sh = this.split.el.getHeight();
35494             box.height += sh;
35495             box.y -= sh;
35496         }
35497         return box;
35498     },
35499     
35500     updateBox : function(box){
35501         if(this.split && !this.collapsed){
35502             var sh = this.split.el.getHeight();
35503             box.height -= sh;
35504             box.y += sh;
35505             this.split.el.setLeft(box.x);
35506             this.split.el.setTop(box.y-sh);
35507             this.split.el.setWidth(box.width);
35508         }
35509         if(this.collapsed){
35510             this.updateBody(box.width, null);
35511         }
35512         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35513     }
35514 });
35515
35516 Roo.EastLayoutRegion = function(mgr, config){
35517     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35518     if(this.split){
35519         this.split.placement = Roo.SplitBar.RIGHT;
35520         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35521         this.split.el.addClass("x-layout-split-h");
35522     }
35523     var size = config.initialSize || config.width;
35524     if(typeof size != "undefined"){
35525         this.el.setWidth(size);
35526     }
35527 };
35528 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35529     orientation: Roo.SplitBar.HORIZONTAL,
35530     getBox : function(){
35531         if(this.collapsed){
35532             return this.collapsedEl.getBox();
35533         }
35534         var box = this.el.getBox();
35535         if(this.split){
35536             var sw = this.split.el.getWidth();
35537             box.width += sw;
35538             box.x -= sw;
35539         }
35540         return box;
35541     },
35542
35543     updateBox : function(box){
35544         if(this.split && !this.collapsed){
35545             var sw = this.split.el.getWidth();
35546             box.width -= sw;
35547             this.split.el.setLeft(box.x);
35548             this.split.el.setTop(box.y);
35549             this.split.el.setHeight(box.height);
35550             box.x += sw;
35551         }
35552         if(this.collapsed){
35553             this.updateBody(null, box.height);
35554         }
35555         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35556     }
35557 });
35558
35559 Roo.WestLayoutRegion = function(mgr, config){
35560     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35561     if(this.split){
35562         this.split.placement = Roo.SplitBar.LEFT;
35563         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35564         this.split.el.addClass("x-layout-split-h");
35565     }
35566     var size = config.initialSize || config.width;
35567     if(typeof size != "undefined"){
35568         this.el.setWidth(size);
35569     }
35570 };
35571 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35572     orientation: Roo.SplitBar.HORIZONTAL,
35573     getBox : function(){
35574         if(this.collapsed){
35575             return this.collapsedEl.getBox();
35576         }
35577         var box = this.el.getBox();
35578         if(this.split){
35579             box.width += this.split.el.getWidth();
35580         }
35581         return box;
35582     },
35583     
35584     updateBox : function(box){
35585         if(this.split && !this.collapsed){
35586             var sw = this.split.el.getWidth();
35587             box.width -= sw;
35588             this.split.el.setLeft(box.x+box.width);
35589             this.split.el.setTop(box.y);
35590             this.split.el.setHeight(box.height);
35591         }
35592         if(this.collapsed){
35593             this.updateBody(null, box.height);
35594         }
35595         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35596     }
35597 });
35598 /*
35599  * Based on:
35600  * Ext JS Library 1.1.1
35601  * Copyright(c) 2006-2007, Ext JS, LLC.
35602  *
35603  * Originally Released Under LGPL - original licence link has changed is not relivant.
35604  *
35605  * Fork - LGPL
35606  * <script type="text/javascript">
35607  */
35608  
35609  
35610 /*
35611  * Private internal class for reading and applying state
35612  */
35613 Roo.LayoutStateManager = function(layout){
35614      // default empty state
35615      this.state = {
35616         north: {},
35617         south: {},
35618         east: {},
35619         west: {}       
35620     };
35621 };
35622
35623 Roo.LayoutStateManager.prototype = {
35624     init : function(layout, provider){
35625         this.provider = provider;
35626         var state = provider.get(layout.id+"-layout-state");
35627         if(state){
35628             var wasUpdating = layout.isUpdating();
35629             if(!wasUpdating){
35630                 layout.beginUpdate();
35631             }
35632             for(var key in state){
35633                 if(typeof state[key] != "function"){
35634                     var rstate = state[key];
35635                     var r = layout.getRegion(key);
35636                     if(r && rstate){
35637                         if(rstate.size){
35638                             r.resizeTo(rstate.size);
35639                         }
35640                         if(rstate.collapsed == true){
35641                             r.collapse(true);
35642                         }else{
35643                             r.expand(null, true);
35644                         }
35645                     }
35646                 }
35647             }
35648             if(!wasUpdating){
35649                 layout.endUpdate();
35650             }
35651             this.state = state; 
35652         }
35653         this.layout = layout;
35654         layout.on("regionresized", this.onRegionResized, this);
35655         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35656         layout.on("regionexpanded", this.onRegionExpanded, this);
35657     },
35658     
35659     storeState : function(){
35660         this.provider.set(this.layout.id+"-layout-state", this.state);
35661     },
35662     
35663     onRegionResized : function(region, newSize){
35664         this.state[region.getPosition()].size = newSize;
35665         this.storeState();
35666     },
35667     
35668     onRegionCollapsed : function(region){
35669         this.state[region.getPosition()].collapsed = true;
35670         this.storeState();
35671     },
35672     
35673     onRegionExpanded : function(region){
35674         this.state[region.getPosition()].collapsed = false;
35675         this.storeState();
35676     }
35677 };/*
35678  * Based on:
35679  * Ext JS Library 1.1.1
35680  * Copyright(c) 2006-2007, Ext JS, LLC.
35681  *
35682  * Originally Released Under LGPL - original licence link has changed is not relivant.
35683  *
35684  * Fork - LGPL
35685  * <script type="text/javascript">
35686  */
35687 /**
35688  * @class Roo.ContentPanel
35689  * @extends Roo.util.Observable
35690  * @children Roo.form.Form Roo.JsonView Roo.View
35691  * @parent Roo.BorderLayout Roo.LayoutDialog builder
35692  * A basic ContentPanel element.
35693  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35694  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35695  * @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
35696  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35697  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35698  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35699  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
35700  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35701  * @cfg {String} title          The title for this panel
35702  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35703  * @cfg {String} url            Calls {@link #setUrl} with this value
35704  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35705  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35706  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35707  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35708  * @cfg {String}    style  Extra style to add to the content panel
35709  * @cfg {Roo.menu.Menu} menu  popup menu
35710
35711  * @constructor
35712  * Create a new ContentPanel.
35713  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35714  * @param {String/Object} config A string to set only the title or a config object
35715  * @param {String} content (optional) Set the HTML content for this panel
35716  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35717  */
35718 Roo.ContentPanel = function(el, config, content){
35719     
35720      
35721     /*
35722     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35723         config = el;
35724         el = Roo.id();
35725     }
35726     if (config && config.parentLayout) { 
35727         el = config.parentLayout.el.createChild(); 
35728     }
35729     */
35730     if(el.autoCreate){ // xtype is available if this is called from factory
35731         config = el;
35732         el = Roo.id();
35733     }
35734     this.el = Roo.get(el);
35735     if(!this.el && config && config.autoCreate){
35736         if(typeof config.autoCreate == "object"){
35737             if(!config.autoCreate.id){
35738                 config.autoCreate.id = config.id||el;
35739             }
35740             this.el = Roo.DomHelper.append(document.body,
35741                         config.autoCreate, true);
35742         }else{
35743             this.el = Roo.DomHelper.append(document.body,
35744                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35745         }
35746     }
35747     
35748     
35749     this.closable = false;
35750     this.loaded = false;
35751     this.active = false;
35752     if(typeof config == "string"){
35753         this.title = config;
35754     }else{
35755         Roo.apply(this, config);
35756     }
35757     
35758     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35759         this.wrapEl = this.el.wrap();
35760         this.toolbar.container = this.el.insertSibling(false, 'before');
35761         this.toolbar = new Roo.Toolbar(this.toolbar);
35762     }
35763     
35764     // xtype created footer. - not sure if will work as we normally have to render first..
35765     if (this.footer && !this.footer.el && this.footer.xtype) {
35766         if (!this.wrapEl) {
35767             this.wrapEl = this.el.wrap();
35768         }
35769     
35770         this.footer.container = this.wrapEl.createChild();
35771          
35772         this.footer = Roo.factory(this.footer, Roo);
35773         
35774     }
35775     
35776     if(this.resizeEl){
35777         this.resizeEl = Roo.get(this.resizeEl, true);
35778     }else{
35779         this.resizeEl = this.el;
35780     }
35781     // handle view.xtype
35782     
35783  
35784     
35785     
35786     this.addEvents({
35787         /**
35788          * @event activate
35789          * Fires when this panel is activated. 
35790          * @param {Roo.ContentPanel} this
35791          */
35792         "activate" : true,
35793         /**
35794          * @event deactivate
35795          * Fires when this panel is activated. 
35796          * @param {Roo.ContentPanel} this
35797          */
35798         "deactivate" : true,
35799
35800         /**
35801          * @event resize
35802          * Fires when this panel is resized if fitToFrame is true.
35803          * @param {Roo.ContentPanel} this
35804          * @param {Number} width The width after any component adjustments
35805          * @param {Number} height The height after any component adjustments
35806          */
35807         "resize" : true,
35808         
35809          /**
35810          * @event render
35811          * Fires when this tab is created
35812          * @param {Roo.ContentPanel} this
35813          */
35814         "render" : true
35815          
35816         
35817     });
35818     
35819
35820     
35821     
35822     if(this.autoScroll){
35823         this.resizeEl.setStyle("overflow", "auto");
35824     } else {
35825         // fix randome scrolling
35826         this.el.on('scroll', function() {
35827             Roo.log('fix random scolling');
35828             this.scrollTo('top',0); 
35829         });
35830     }
35831     content = content || this.content;
35832     if(content){
35833         this.setContent(content);
35834     }
35835     if(config && config.url){
35836         this.setUrl(this.url, this.params, this.loadOnce);
35837     }
35838     
35839     
35840     
35841     Roo.ContentPanel.superclass.constructor.call(this);
35842     
35843     if (this.view && typeof(this.view.xtype) != 'undefined') {
35844         this.view.el = this.el.appendChild(document.createElement("div"));
35845         this.view = Roo.factory(this.view); 
35846         this.view.render  &&  this.view.render(false, '');  
35847     }
35848     
35849     
35850     this.fireEvent('render', this);
35851 };
35852
35853 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35854     tabTip:'',
35855     setRegion : function(region){
35856         this.region = region;
35857         if(region){
35858            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35859         }else{
35860            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35861         } 
35862     },
35863     
35864     /**
35865      * Returns the toolbar for this Panel if one was configured. 
35866      * @return {Roo.Toolbar} 
35867      */
35868     getToolbar : function(){
35869         return this.toolbar;
35870     },
35871     
35872     setActiveState : function(active){
35873         this.active = active;
35874         if(!active){
35875             this.fireEvent("deactivate", this);
35876         }else{
35877             this.fireEvent("activate", this);
35878         }
35879     },
35880     /**
35881      * Updates this panel's element
35882      * @param {String} content The new content
35883      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35884     */
35885     setContent : function(content, loadScripts){
35886         this.el.update(content, loadScripts);
35887     },
35888
35889     ignoreResize : function(w, h){
35890         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35891             return true;
35892         }else{
35893             this.lastSize = {width: w, height: h};
35894             return false;
35895         }
35896     },
35897     /**
35898      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35899      * @return {Roo.UpdateManager} The UpdateManager
35900      */
35901     getUpdateManager : function(){
35902         return this.el.getUpdateManager();
35903     },
35904      /**
35905      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35906      * @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:
35907 <pre><code>
35908 panel.load({
35909     url: "your-url.php",
35910     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35911     callback: yourFunction,
35912     scope: yourObject, //(optional scope)
35913     discardUrl: false,
35914     nocache: false,
35915     text: "Loading...",
35916     timeout: 30,
35917     scripts: false
35918 });
35919 </code></pre>
35920      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35921      * 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.
35922      * @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}
35923      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35924      * @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.
35925      * @return {Roo.ContentPanel} this
35926      */
35927     load : function(){
35928         var um = this.el.getUpdateManager();
35929         um.update.apply(um, arguments);
35930         return this;
35931     },
35932
35933
35934     /**
35935      * 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.
35936      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35937      * @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)
35938      * @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)
35939      * @return {Roo.UpdateManager} The UpdateManager
35940      */
35941     setUrl : function(url, params, loadOnce){
35942         if(this.refreshDelegate){
35943             this.removeListener("activate", this.refreshDelegate);
35944         }
35945         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35946         this.on("activate", this.refreshDelegate);
35947         return this.el.getUpdateManager();
35948     },
35949     
35950     _handleRefresh : function(url, params, loadOnce){
35951         if(!loadOnce || !this.loaded){
35952             var updater = this.el.getUpdateManager();
35953             updater.update(url, params, this._setLoaded.createDelegate(this));
35954         }
35955     },
35956     
35957     _setLoaded : function(){
35958         this.loaded = true;
35959     }, 
35960     
35961     /**
35962      * Returns this panel's id
35963      * @return {String} 
35964      */
35965     getId : function(){
35966         return this.el.id;
35967     },
35968     
35969     /** 
35970      * Returns this panel's element - used by regiosn to add.
35971      * @return {Roo.Element} 
35972      */
35973     getEl : function(){
35974         return this.wrapEl || this.el;
35975     },
35976     
35977     adjustForComponents : function(width, height)
35978     {
35979         //Roo.log('adjustForComponents ');
35980         if(this.resizeEl != this.el){
35981             width -= this.el.getFrameWidth('lr');
35982             height -= this.el.getFrameWidth('tb');
35983         }
35984         if(this.toolbar){
35985             var te = this.toolbar.getEl();
35986             height -= te.getHeight();
35987             te.setWidth(width);
35988         }
35989         if(this.footer){
35990             var te = this.footer.getEl();
35991             //Roo.log("footer:" + te.getHeight());
35992             
35993             height -= te.getHeight();
35994             te.setWidth(width);
35995         }
35996         
35997         
35998         if(this.adjustments){
35999             width += this.adjustments[0];
36000             height += this.adjustments[1];
36001         }
36002         return {"width": width, "height": height};
36003     },
36004     
36005     setSize : function(width, height){
36006         if(this.fitToFrame && !this.ignoreResize(width, height)){
36007             if(this.fitContainer && this.resizeEl != this.el){
36008                 this.el.setSize(width, height);
36009             }
36010             var size = this.adjustForComponents(width, height);
36011             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36012             this.fireEvent('resize', this, size.width, size.height);
36013         }
36014     },
36015     
36016     /**
36017      * Returns this panel's title
36018      * @return {String} 
36019      */
36020     getTitle : function(){
36021         return this.title;
36022     },
36023     
36024     /**
36025      * Set this panel's title
36026      * @param {String} title
36027      */
36028     setTitle : function(title){
36029         this.title = title;
36030         if(this.region){
36031             this.region.updatePanelTitle(this, title);
36032         }
36033     },
36034     
36035     /**
36036      * Returns true is this panel was configured to be closable
36037      * @return {Boolean} 
36038      */
36039     isClosable : function(){
36040         return this.closable;
36041     },
36042     
36043     beforeSlide : function(){
36044         this.el.clip();
36045         this.resizeEl.clip();
36046     },
36047     
36048     afterSlide : function(){
36049         this.el.unclip();
36050         this.resizeEl.unclip();
36051     },
36052     
36053     /**
36054      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36055      *   Will fail silently if the {@link #setUrl} method has not been called.
36056      *   This does not activate the panel, just updates its content.
36057      */
36058     refresh : function(){
36059         if(this.refreshDelegate){
36060            this.loaded = false;
36061            this.refreshDelegate();
36062         }
36063     },
36064     
36065     /**
36066      * Destroys this panel
36067      */
36068     destroy : function(){
36069         this.el.removeAllListeners();
36070         var tempEl = document.createElement("span");
36071         tempEl.appendChild(this.el.dom);
36072         tempEl.innerHTML = "";
36073         this.el.remove();
36074         this.el = null;
36075     },
36076     
36077     /**
36078      * form - if the content panel contains a form - this is a reference to it.
36079      * @type {Roo.form.Form}
36080      */
36081     form : false,
36082     /**
36083      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36084      *    This contains a reference to it.
36085      * @type {Roo.View}
36086      */
36087     view : false,
36088     
36089       /**
36090      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36091      * <pre><code>
36092
36093 layout.addxtype({
36094        xtype : 'Form',
36095        items: [ .... ]
36096    }
36097 );
36098
36099 </code></pre>
36100      * @param {Object} cfg Xtype definition of item to add.
36101      */
36102     
36103     addxtype : function(cfg) {
36104         // add form..
36105         if (cfg.xtype.match(/^Form$/)) {
36106             
36107             var el;
36108             //if (this.footer) {
36109             //    el = this.footer.container.insertSibling(false, 'before');
36110             //} else {
36111                 el = this.el.createChild();
36112             //}
36113
36114             this.form = new  Roo.form.Form(cfg);
36115             
36116             
36117             if ( this.form.allItems.length) {
36118                 this.form.render(el.dom);
36119             }
36120             return this.form;
36121         }
36122         // should only have one of theses..
36123         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36124             // views.. should not be just added - used named prop 'view''
36125             
36126             cfg.el = this.el.appendChild(document.createElement("div"));
36127             // factory?
36128             
36129             var ret = new Roo.factory(cfg);
36130              
36131              ret.render && ret.render(false, ''); // render blank..
36132             this.view = ret;
36133             return ret;
36134         }
36135         return false;
36136     }
36137 });
36138
36139
36140
36141
36142
36143
36144
36145
36146
36147
36148
36149
36150 /**
36151  * @class Roo.GridPanel
36152  * @extends Roo.ContentPanel
36153  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36154  * @constructor
36155  * Create a new GridPanel.
36156  * @cfg {Roo.grid.Grid} grid The grid for this panel
36157  */
36158 Roo.GridPanel = function(grid, config){
36159     
36160     // universal ctor...
36161     if (typeof(grid.grid) != 'undefined') {
36162         config = grid;
36163         grid = config.grid;
36164     }
36165     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36166         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36167         
36168     this.wrapper.dom.appendChild(grid.getGridEl().dom);
36169     
36170     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36171     
36172     if(this.toolbar){
36173         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36174     }
36175     // xtype created footer. - not sure if will work as we normally have to render first..
36176     if (this.footer && !this.footer.el && this.footer.xtype) {
36177         
36178         this.footer.container = this.grid.getView().getFooterPanel(true);
36179         this.footer.dataSource = this.grid.dataSource;
36180         this.footer = Roo.factory(this.footer, Roo);
36181         
36182     }
36183     
36184     grid.monitorWindowResize = false; // turn off autosizing
36185     grid.autoHeight = false;
36186     grid.autoWidth = false;
36187     this.grid = grid;
36188     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36189 };
36190
36191 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36192     getId : function(){
36193         return this.grid.id;
36194     },
36195     
36196     /**
36197      * Returns the grid for this panel
36198      * @return {Roo.grid.Grid} 
36199      */
36200     getGrid : function(){
36201         return this.grid;    
36202     },
36203     
36204     setSize : function(width, height){
36205         if(!this.ignoreResize(width, height)){
36206             var grid = this.grid;
36207             var size = this.adjustForComponents(width, height);
36208             grid.getGridEl().setSize(size.width, size.height);
36209             grid.autoSize();
36210         }
36211     },
36212     
36213     beforeSlide : function(){
36214         this.grid.getView().scroller.clip();
36215     },
36216     
36217     afterSlide : function(){
36218         this.grid.getView().scroller.unclip();
36219     },
36220     
36221     destroy : function(){
36222         this.grid.destroy();
36223         delete this.grid;
36224         Roo.GridPanel.superclass.destroy.call(this); 
36225     }
36226 });
36227
36228
36229 /**
36230  * @class Roo.NestedLayoutPanel
36231  * @extends Roo.ContentPanel
36232  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36233  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
36234  *
36235  * 
36236  * @constructor
36237  * Create a new NestedLayoutPanel.
36238  * 
36239  * 
36240  * @param {Roo.BorderLayout} layout [required] The layout for this panel
36241  * @param {String/Object} config A string to set only the title or a config object
36242  */
36243 Roo.NestedLayoutPanel = function(layout, config)
36244 {
36245     // construct with only one argument..
36246     /* FIXME - implement nicer consturctors
36247     if (layout.layout) {
36248         config = layout;
36249         layout = config.layout;
36250         delete config.layout;
36251     }
36252     if (layout.xtype && !layout.getEl) {
36253         // then layout needs constructing..
36254         layout = Roo.factory(layout, Roo);
36255     }
36256     */
36257     
36258     
36259     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36260     
36261     layout.monitorWindowResize = false; // turn off autosizing
36262     this.layout = layout;
36263     this.layout.getEl().addClass("x-layout-nested-layout");
36264     
36265     
36266     
36267     
36268 };
36269
36270 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36271
36272     layout : false,
36273
36274     setSize : function(width, height){
36275         if(!this.ignoreResize(width, height)){
36276             var size = this.adjustForComponents(width, height);
36277             var el = this.layout.getEl();
36278             el.setSize(size.width, size.height);
36279             var touch = el.dom.offsetWidth;
36280             this.layout.layout();
36281             // ie requires a double layout on the first pass
36282             if(Roo.isIE && !this.initialized){
36283                 this.initialized = true;
36284                 this.layout.layout();
36285             }
36286         }
36287     },
36288     
36289     // activate all subpanels if not currently active..
36290     
36291     setActiveState : function(active){
36292         this.active = active;
36293         if(!active){
36294             this.fireEvent("deactivate", this);
36295             return;
36296         }
36297         
36298         this.fireEvent("activate", this);
36299         // not sure if this should happen before or after..
36300         if (!this.layout) {
36301             return; // should not happen..
36302         }
36303         var reg = false;
36304         for (var r in this.layout.regions) {
36305             reg = this.layout.getRegion(r);
36306             if (reg.getActivePanel()) {
36307                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36308                 reg.setActivePanel(reg.getActivePanel());
36309                 continue;
36310             }
36311             if (!reg.panels.length) {
36312                 continue;
36313             }
36314             reg.showPanel(reg.getPanel(0));
36315         }
36316         
36317         
36318         
36319         
36320     },
36321     
36322     /**
36323      * Returns the nested BorderLayout for this panel
36324      * @return {Roo.BorderLayout}
36325      */
36326     getLayout : function(){
36327         return this.layout;
36328     },
36329     
36330      /**
36331      * Adds a xtype elements to the layout of the nested panel
36332      * <pre><code>
36333
36334 panel.addxtype({
36335        xtype : 'ContentPanel',
36336        region: 'west',
36337        items: [ .... ]
36338    }
36339 );
36340
36341 panel.addxtype({
36342         xtype : 'NestedLayoutPanel',
36343         region: 'west',
36344         layout: {
36345            center: { },
36346            west: { }   
36347         },
36348         items : [ ... list of content panels or nested layout panels.. ]
36349    }
36350 );
36351 </code></pre>
36352      * @param {Object} cfg Xtype definition of item to add.
36353      */
36354     addxtype : function(cfg) {
36355         return this.layout.addxtype(cfg);
36356     
36357     }
36358 });
36359
36360 Roo.ScrollPanel = function(el, config, content){
36361     config = config || {};
36362     config.fitToFrame = true;
36363     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36364     
36365     this.el.dom.style.overflow = "hidden";
36366     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36367     this.el.removeClass("x-layout-inactive-content");
36368     this.el.on("mousewheel", this.onWheel, this);
36369
36370     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
36371     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
36372     up.unselectable(); down.unselectable();
36373     up.on("click", this.scrollUp, this);
36374     down.on("click", this.scrollDown, this);
36375     up.addClassOnOver("x-scroller-btn-over");
36376     down.addClassOnOver("x-scroller-btn-over");
36377     up.addClassOnClick("x-scroller-btn-click");
36378     down.addClassOnClick("x-scroller-btn-click");
36379     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36380
36381     this.resizeEl = this.el;
36382     this.el = wrap; this.up = up; this.down = down;
36383 };
36384
36385 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36386     increment : 100,
36387     wheelIncrement : 5,
36388     scrollUp : function(){
36389         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36390     },
36391
36392     scrollDown : function(){
36393         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36394     },
36395
36396     afterScroll : function(){
36397         var el = this.resizeEl;
36398         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36399         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36400         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36401     },
36402
36403     setSize : function(){
36404         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36405         this.afterScroll();
36406     },
36407
36408     onWheel : function(e){
36409         var d = e.getWheelDelta();
36410         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36411         this.afterScroll();
36412         e.stopEvent();
36413     },
36414
36415     setContent : function(content, loadScripts){
36416         this.resizeEl.update(content, loadScripts);
36417     }
36418
36419 });
36420
36421
36422
36423 /**
36424  * @class Roo.TreePanel
36425  * @extends Roo.ContentPanel
36426  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36427  * Treepanel component
36428  * 
36429  * @constructor
36430  * Create a new TreePanel. - defaults to fit/scoll contents.
36431  * @param {String/Object} config A string to set only the panel's title, or a config object
36432  */
36433 Roo.TreePanel = function(config){
36434     var el = config.el;
36435     var tree = config.tree;
36436     delete config.tree; 
36437     delete config.el; // hopefull!
36438     
36439     // wrapper for IE7 strict & safari scroll issue
36440     
36441     var treeEl = el.createChild();
36442     config.resizeEl = treeEl;
36443     
36444     
36445     
36446     Roo.TreePanel.superclass.constructor.call(this, el, config);
36447  
36448  
36449     this.tree = new Roo.tree.TreePanel(treeEl , tree);
36450     //console.log(tree);
36451     this.on('activate', function()
36452     {
36453         if (this.tree.rendered) {
36454             return;
36455         }
36456         //console.log('render tree');
36457         this.tree.render();
36458     });
36459     // this should not be needed.. - it's actually the 'el' that resizes?
36460     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36461     
36462     //this.on('resize',  function (cp, w, h) {
36463     //        this.tree.innerCt.setWidth(w);
36464     //        this.tree.innerCt.setHeight(h);
36465     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
36466     //});
36467
36468         
36469     
36470 };
36471
36472 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
36473     fitToFrame : true,
36474     autoScroll : true,
36475     /*
36476      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36477      */
36478     tree : false
36479
36480 });
36481 /*
36482  * Based on:
36483  * Ext JS Library 1.1.1
36484  * Copyright(c) 2006-2007, Ext JS, LLC.
36485  *
36486  * Originally Released Under LGPL - original licence link has changed is not relivant.
36487  *
36488  * Fork - LGPL
36489  * <script type="text/javascript">
36490  */
36491  
36492
36493 /**
36494  * @class Roo.ReaderLayout
36495  * @extends Roo.BorderLayout
36496  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
36497  * center region containing two nested regions (a top one for a list view and one for item preview below),
36498  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36499  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36500  * expedites the setup of the overall layout and regions for this common application style.
36501  * Example:
36502  <pre><code>
36503 var reader = new Roo.ReaderLayout();
36504 var CP = Roo.ContentPanel;  // shortcut for adding
36505
36506 reader.beginUpdate();
36507 reader.add("north", new CP("north", "North"));
36508 reader.add("west", new CP("west", {title: "West"}));
36509 reader.add("east", new CP("east", {title: "East"}));
36510
36511 reader.regions.listView.add(new CP("listView", "List"));
36512 reader.regions.preview.add(new CP("preview", "Preview"));
36513 reader.endUpdate();
36514 </code></pre>
36515 * @constructor
36516 * Create a new ReaderLayout
36517 * @param {Object} config Configuration options
36518 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36519 * document.body if omitted)
36520 */
36521 Roo.ReaderLayout = function(config, renderTo){
36522     var c = config || {size:{}};
36523     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36524         north: c.north !== false ? Roo.apply({
36525             split:false,
36526             initialSize: 32,
36527             titlebar: false
36528         }, c.north) : false,
36529         west: c.west !== false ? Roo.apply({
36530             split:true,
36531             initialSize: 200,
36532             minSize: 175,
36533             maxSize: 400,
36534             titlebar: true,
36535             collapsible: true,
36536             animate: true,
36537             margins:{left:5,right:0,bottom:5,top:5},
36538             cmargins:{left:5,right:5,bottom:5,top:5}
36539         }, c.west) : false,
36540         east: c.east !== false ? Roo.apply({
36541             split:true,
36542             initialSize: 200,
36543             minSize: 175,
36544             maxSize: 400,
36545             titlebar: true,
36546             collapsible: true,
36547             animate: true,
36548             margins:{left:0,right:5,bottom:5,top:5},
36549             cmargins:{left:5,right:5,bottom:5,top:5}
36550         }, c.east) : false,
36551         center: Roo.apply({
36552             tabPosition: 'top',
36553             autoScroll:false,
36554             closeOnTab: true,
36555             titlebar:false,
36556             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36557         }, c.center)
36558     });
36559
36560     this.el.addClass('x-reader');
36561
36562     this.beginUpdate();
36563
36564     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36565         south: c.preview !== false ? Roo.apply({
36566             split:true,
36567             initialSize: 200,
36568             minSize: 100,
36569             autoScroll:true,
36570             collapsible:true,
36571             titlebar: true,
36572             cmargins:{top:5,left:0, right:0, bottom:0}
36573         }, c.preview) : false,
36574         center: Roo.apply({
36575             autoScroll:false,
36576             titlebar:false,
36577             minHeight:200
36578         }, c.listView)
36579     });
36580     this.add('center', new Roo.NestedLayoutPanel(inner,
36581             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36582
36583     this.endUpdate();
36584
36585     this.regions.preview = inner.getRegion('south');
36586     this.regions.listView = inner.getRegion('center');
36587 };
36588
36589 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36590  * Based on:
36591  * Ext JS Library 1.1.1
36592  * Copyright(c) 2006-2007, Ext JS, LLC.
36593  *
36594  * Originally Released Under LGPL - original licence link has changed is not relivant.
36595  *
36596  * Fork - LGPL
36597  * <script type="text/javascript">
36598  */
36599  
36600 /**
36601  * @class Roo.grid.Grid
36602  * @extends Roo.util.Observable
36603  * This class represents the primary interface of a component based grid control.
36604  * <br><br>Usage:<pre><code>
36605  var grid = new Roo.grid.Grid("my-container-id", {
36606      ds: myDataStore,
36607      cm: myColModel,
36608      selModel: mySelectionModel,
36609      autoSizeColumns: true,
36610      monitorWindowResize: false,
36611      trackMouseOver: true
36612  });
36613  // set any options
36614  grid.render();
36615  * </code></pre>
36616  * <b>Common Problems:</b><br/>
36617  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36618  * element will correct this<br/>
36619  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36620  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36621  * are unpredictable.<br/>
36622  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36623  * grid to calculate dimensions/offsets.<br/>
36624   * @constructor
36625  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36626  * The container MUST have some type of size defined for the grid to fill. The container will be
36627  * automatically set to position relative if it isn't already.
36628  * @param {Object} config A config object that sets properties on this grid.
36629  */
36630 Roo.grid.Grid = function(container, config){
36631         // initialize the container
36632         this.container = Roo.get(container);
36633         this.container.update("");
36634         this.container.setStyle("overflow", "hidden");
36635     this.container.addClass('x-grid-container');
36636
36637     this.id = this.container.id;
36638
36639     Roo.apply(this, config);
36640     // check and correct shorthanded configs
36641     if(this.ds){
36642         this.dataSource = this.ds;
36643         delete this.ds;
36644     }
36645     if(this.cm){
36646         this.colModel = this.cm;
36647         delete this.cm;
36648     }
36649     if(this.sm){
36650         this.selModel = this.sm;
36651         delete this.sm;
36652     }
36653
36654     if (this.selModel) {
36655         this.selModel = Roo.factory(this.selModel, Roo.grid);
36656         this.sm = this.selModel;
36657         this.sm.xmodule = this.xmodule || false;
36658     }
36659     if (typeof(this.colModel.config) == 'undefined') {
36660         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36661         this.cm = this.colModel;
36662         this.cm.xmodule = this.xmodule || false;
36663     }
36664     if (this.dataSource) {
36665         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36666         this.ds = this.dataSource;
36667         this.ds.xmodule = this.xmodule || false;
36668          
36669     }
36670     
36671     
36672     
36673     if(this.width){
36674         this.container.setWidth(this.width);
36675     }
36676
36677     if(this.height){
36678         this.container.setHeight(this.height);
36679     }
36680     /** @private */
36681         this.addEvents({
36682         // raw events
36683         /**
36684          * @event click
36685          * The raw click event for the entire grid.
36686          * @param {Roo.EventObject} e
36687          */
36688         "click" : true,
36689         /**
36690          * @event dblclick
36691          * The raw dblclick event for the entire grid.
36692          * @param {Roo.EventObject} e
36693          */
36694         "dblclick" : true,
36695         /**
36696          * @event contextmenu
36697          * The raw contextmenu event for the entire grid.
36698          * @param {Roo.EventObject} e
36699          */
36700         "contextmenu" : true,
36701         /**
36702          * @event mousedown
36703          * The raw mousedown event for the entire grid.
36704          * @param {Roo.EventObject} e
36705          */
36706         "mousedown" : true,
36707         /**
36708          * @event mouseup
36709          * The raw mouseup event for the entire grid.
36710          * @param {Roo.EventObject} e
36711          */
36712         "mouseup" : true,
36713         /**
36714          * @event mouseover
36715          * The raw mouseover event for the entire grid.
36716          * @param {Roo.EventObject} e
36717          */
36718         "mouseover" : true,
36719         /**
36720          * @event mouseout
36721          * The raw mouseout event for the entire grid.
36722          * @param {Roo.EventObject} e
36723          */
36724         "mouseout" : true,
36725         /**
36726          * @event keypress
36727          * The raw keypress event for the entire grid.
36728          * @param {Roo.EventObject} e
36729          */
36730         "keypress" : true,
36731         /**
36732          * @event keydown
36733          * The raw keydown event for the entire grid.
36734          * @param {Roo.EventObject} e
36735          */
36736         "keydown" : true,
36737
36738         // custom events
36739
36740         /**
36741          * @event cellclick
36742          * Fires when a cell is clicked
36743          * @param {Grid} this
36744          * @param {Number} rowIndex
36745          * @param {Number} columnIndex
36746          * @param {Roo.EventObject} e
36747          */
36748         "cellclick" : true,
36749         /**
36750          * @event celldblclick
36751          * Fires when a cell is double clicked
36752          * @param {Grid} this
36753          * @param {Number} rowIndex
36754          * @param {Number} columnIndex
36755          * @param {Roo.EventObject} e
36756          */
36757         "celldblclick" : true,
36758         /**
36759          * @event rowclick
36760          * Fires when a row is clicked
36761          * @param {Grid} this
36762          * @param {Number} rowIndex
36763          * @param {Roo.EventObject} e
36764          */
36765         "rowclick" : true,
36766         /**
36767          * @event rowdblclick
36768          * Fires when a row is double clicked
36769          * @param {Grid} this
36770          * @param {Number} rowIndex
36771          * @param {Roo.EventObject} e
36772          */
36773         "rowdblclick" : true,
36774         /**
36775          * @event headerclick
36776          * Fires when a header is clicked
36777          * @param {Grid} this
36778          * @param {Number} columnIndex
36779          * @param {Roo.EventObject} e
36780          */
36781         "headerclick" : true,
36782         /**
36783          * @event headerdblclick
36784          * Fires when a header cell is double clicked
36785          * @param {Grid} this
36786          * @param {Number} columnIndex
36787          * @param {Roo.EventObject} e
36788          */
36789         "headerdblclick" : true,
36790         /**
36791          * @event rowcontextmenu
36792          * Fires when a row is right clicked
36793          * @param {Grid} this
36794          * @param {Number} rowIndex
36795          * @param {Roo.EventObject} e
36796          */
36797         "rowcontextmenu" : true,
36798         /**
36799          * @event cellcontextmenu
36800          * Fires when a cell is right clicked
36801          * @param {Grid} this
36802          * @param {Number} rowIndex
36803          * @param {Number} cellIndex
36804          * @param {Roo.EventObject} e
36805          */
36806          "cellcontextmenu" : true,
36807         /**
36808          * @event headercontextmenu
36809          * Fires when a header is right clicked
36810          * @param {Grid} this
36811          * @param {Number} columnIndex
36812          * @param {Roo.EventObject} e
36813          */
36814         "headercontextmenu" : true,
36815         /**
36816          * @event bodyscroll
36817          * Fires when the body element is scrolled
36818          * @param {Number} scrollLeft
36819          * @param {Number} scrollTop
36820          */
36821         "bodyscroll" : true,
36822         /**
36823          * @event columnresize
36824          * Fires when the user resizes a column
36825          * @param {Number} columnIndex
36826          * @param {Number} newSize
36827          */
36828         "columnresize" : true,
36829         /**
36830          * @event columnmove
36831          * Fires when the user moves a column
36832          * @param {Number} oldIndex
36833          * @param {Number} newIndex
36834          */
36835         "columnmove" : true,
36836         /**
36837          * @event startdrag
36838          * Fires when row(s) start being dragged
36839          * @param {Grid} this
36840          * @param {Roo.GridDD} dd The drag drop object
36841          * @param {event} e The raw browser event
36842          */
36843         "startdrag" : true,
36844         /**
36845          * @event enddrag
36846          * Fires when a drag operation is complete
36847          * @param {Grid} this
36848          * @param {Roo.GridDD} dd The drag drop object
36849          * @param {event} e The raw browser event
36850          */
36851         "enddrag" : true,
36852         /**
36853          * @event dragdrop
36854          * Fires when dragged row(s) are dropped on a valid DD target
36855          * @param {Grid} this
36856          * @param {Roo.GridDD} dd The drag drop object
36857          * @param {String} targetId The target drag drop object
36858          * @param {event} e The raw browser event
36859          */
36860         "dragdrop" : true,
36861         /**
36862          * @event dragover
36863          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36864          * @param {Grid} this
36865          * @param {Roo.GridDD} dd The drag drop object
36866          * @param {String} targetId The target drag drop object
36867          * @param {event} e The raw browser event
36868          */
36869         "dragover" : true,
36870         /**
36871          * @event dragenter
36872          *  Fires when the dragged row(s) first cross another DD target while being dragged
36873          * @param {Grid} this
36874          * @param {Roo.GridDD} dd The drag drop object
36875          * @param {String} targetId The target drag drop object
36876          * @param {event} e The raw browser event
36877          */
36878         "dragenter" : true,
36879         /**
36880          * @event dragout
36881          * Fires when the dragged row(s) leave another DD target while being dragged
36882          * @param {Grid} this
36883          * @param {Roo.GridDD} dd The drag drop object
36884          * @param {String} targetId The target drag drop object
36885          * @param {event} e The raw browser event
36886          */
36887         "dragout" : true,
36888         /**
36889          * @event rowclass
36890          * Fires when a row is rendered, so you can change add a style to it.
36891          * @param {GridView} gridview   The grid view
36892          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36893          */
36894         'rowclass' : true,
36895
36896         /**
36897          * @event render
36898          * Fires when the grid is rendered
36899          * @param {Grid} grid
36900          */
36901         'render' : true
36902     });
36903
36904     Roo.grid.Grid.superclass.constructor.call(this);
36905 };
36906 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36907     
36908     /**
36909          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
36910          */
36911         /**
36912          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
36913          */
36914         /**
36915          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
36916          */
36917         /**
36918          * @cfg {Roo.grid.Store} ds The data store for the grid
36919          */
36920         /**
36921          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
36922          */
36923         /**
36924      * @cfg {String} ddGroup - drag drop group.
36925      */
36926       /**
36927      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
36928      */
36929
36930     /**
36931      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36932      */
36933     minColumnWidth : 25,
36934
36935     /**
36936      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36937      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36938      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36939      */
36940     autoSizeColumns : false,
36941
36942     /**
36943      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36944      */
36945     autoSizeHeaders : true,
36946
36947     /**
36948      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36949      */
36950     monitorWindowResize : true,
36951
36952     /**
36953      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36954      * rows measured to get a columns size. Default is 0 (all rows).
36955      */
36956     maxRowsToMeasure : 0,
36957
36958     /**
36959      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36960      */
36961     trackMouseOver : true,
36962
36963     /**
36964     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36965     */
36966       /**
36967     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
36968     */
36969     
36970     /**
36971     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36972     */
36973     enableDragDrop : false,
36974     
36975     /**
36976     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36977     */
36978     enableColumnMove : true,
36979     
36980     /**
36981     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36982     */
36983     enableColumnHide : true,
36984     
36985     /**
36986     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36987     */
36988     enableRowHeightSync : false,
36989     
36990     /**
36991     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36992     */
36993     stripeRows : true,
36994     
36995     /**
36996     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36997     */
36998     autoHeight : false,
36999
37000     /**
37001      * @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.
37002      */
37003     autoExpandColumn : false,
37004
37005     /**
37006     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
37007     * Default is 50.
37008     */
37009     autoExpandMin : 50,
37010
37011     /**
37012     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
37013     */
37014     autoExpandMax : 1000,
37015
37016     /**
37017     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
37018     */
37019     view : null,
37020
37021     /**
37022     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
37023     */
37024     loadMask : false,
37025     /**
37026     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
37027     */
37028     dropTarget: false,
37029      /**
37030     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
37031     */ 
37032     sortColMenu : false,
37033     
37034     // private
37035     rendered : false,
37036
37037     /**
37038     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37039     * of a fixed width. Default is false.
37040     */
37041     /**
37042     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37043     */
37044     
37045     
37046     /**
37047     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37048     * %0 is replaced with the number of selected rows.
37049     */
37050     ddText : "{0} selected row{1}",
37051     
37052     
37053     /**
37054      * Called once after all setup has been completed and the grid is ready to be rendered.
37055      * @return {Roo.grid.Grid} this
37056      */
37057     render : function()
37058     {
37059         var c = this.container;
37060         // try to detect autoHeight/width mode
37061         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37062             this.autoHeight = true;
37063         }
37064         var view = this.getView();
37065         view.init(this);
37066
37067         c.on("click", this.onClick, this);
37068         c.on("dblclick", this.onDblClick, this);
37069         c.on("contextmenu", this.onContextMenu, this);
37070         c.on("keydown", this.onKeyDown, this);
37071         if (Roo.isTouch) {
37072             c.on("touchstart", this.onTouchStart, this);
37073         }
37074
37075         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37076
37077         this.getSelectionModel().init(this);
37078
37079         view.render();
37080
37081         if(this.loadMask){
37082             this.loadMask = new Roo.LoadMask(this.container,
37083                     Roo.apply({store:this.dataSource}, this.loadMask));
37084         }
37085         
37086         
37087         if (this.toolbar && this.toolbar.xtype) {
37088             this.toolbar.container = this.getView().getHeaderPanel(true);
37089             this.toolbar = new Roo.Toolbar(this.toolbar);
37090         }
37091         if (this.footer && this.footer.xtype) {
37092             this.footer.dataSource = this.getDataSource();
37093             this.footer.container = this.getView().getFooterPanel(true);
37094             this.footer = Roo.factory(this.footer, Roo);
37095         }
37096         if (this.dropTarget && this.dropTarget.xtype) {
37097             delete this.dropTarget.xtype;
37098             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37099         }
37100         
37101         
37102         this.rendered = true;
37103         this.fireEvent('render', this);
37104         return this;
37105     },
37106
37107     /**
37108      * Reconfigures the grid to use a different Store and Column Model.
37109      * The View will be bound to the new objects and refreshed.
37110      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37111      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37112      */
37113     reconfigure : function(dataSource, colModel){
37114         if(this.loadMask){
37115             this.loadMask.destroy();
37116             this.loadMask = new Roo.LoadMask(this.container,
37117                     Roo.apply({store:dataSource}, this.loadMask));
37118         }
37119         this.view.bind(dataSource, colModel);
37120         this.dataSource = dataSource;
37121         this.colModel = colModel;
37122         this.view.refresh(true);
37123     },
37124     /**
37125      * addColumns
37126      * Add's a column, default at the end..
37127      
37128      * @param {int} position to add (default end)
37129      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
37130      */
37131     addColumns : function(pos, ar)
37132     {
37133         
37134         for (var i =0;i< ar.length;i++) {
37135             var cfg = ar[i];
37136             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37137             this.cm.lookup[cfg.id] = cfg;
37138         }
37139         
37140         
37141         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37142             pos = this.cm.config.length; //this.cm.config.push(cfg);
37143         } 
37144         pos = Math.max(0,pos);
37145         ar.unshift(0);
37146         ar.unshift(pos);
37147         this.cm.config.splice.apply(this.cm.config, ar);
37148         
37149         
37150         
37151         this.view.generateRules(this.cm);
37152         this.view.refresh(true);
37153         
37154     },
37155     
37156     
37157     
37158     
37159     // private
37160     onKeyDown : function(e){
37161         this.fireEvent("keydown", e);
37162     },
37163
37164     /**
37165      * Destroy this grid.
37166      * @param {Boolean} removeEl True to remove the element
37167      */
37168     destroy : function(removeEl, keepListeners){
37169         if(this.loadMask){
37170             this.loadMask.destroy();
37171         }
37172         var c = this.container;
37173         c.removeAllListeners();
37174         this.view.destroy();
37175         this.colModel.purgeListeners();
37176         if(!keepListeners){
37177             this.purgeListeners();
37178         }
37179         c.update("");
37180         if(removeEl === true){
37181             c.remove();
37182         }
37183     },
37184
37185     // private
37186     processEvent : function(name, e){
37187         // does this fire select???
37188         //Roo.log('grid:processEvent '  + name);
37189         
37190         if (name != 'touchstart' ) {
37191             this.fireEvent(name, e);    
37192         }
37193         
37194         var t = e.getTarget();
37195         var v = this.view;
37196         var header = v.findHeaderIndex(t);
37197         if(header !== false){
37198             var ename = name == 'touchstart' ? 'click' : name;
37199              
37200             this.fireEvent("header" + ename, this, header, e);
37201         }else{
37202             var row = v.findRowIndex(t);
37203             var cell = v.findCellIndex(t);
37204             if (name == 'touchstart') {
37205                 // first touch is always a click.
37206                 // hopefull this happens after selection is updated.?
37207                 name = false;
37208                 
37209                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37210                     var cs = this.selModel.getSelectedCell();
37211                     if (row == cs[0] && cell == cs[1]){
37212                         name = 'dblclick';
37213                     }
37214                 }
37215                 if (typeof(this.selModel.getSelections) != 'undefined') {
37216                     var cs = this.selModel.getSelections();
37217                     var ds = this.dataSource;
37218                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
37219                         name = 'dblclick';
37220                     }
37221                 }
37222                 if (!name) {
37223                     return;
37224                 }
37225             }
37226             
37227             
37228             if(row !== false){
37229                 this.fireEvent("row" + name, this, row, e);
37230                 if(cell !== false){
37231                     this.fireEvent("cell" + name, this, row, cell, e);
37232                 }
37233             }
37234         }
37235     },
37236
37237     // private
37238     onClick : function(e){
37239         this.processEvent("click", e);
37240     },
37241    // private
37242     onTouchStart : function(e){
37243         this.processEvent("touchstart", e);
37244     },
37245
37246     // private
37247     onContextMenu : function(e, t){
37248         this.processEvent("contextmenu", e);
37249     },
37250
37251     // private
37252     onDblClick : function(e){
37253         this.processEvent("dblclick", e);
37254     },
37255
37256     // private
37257     walkCells : function(row, col, step, fn, scope){
37258         var cm = this.colModel, clen = cm.getColumnCount();
37259         var ds = this.dataSource, rlen = ds.getCount(), first = true;
37260         if(step < 0){
37261             if(col < 0){
37262                 row--;
37263                 first = false;
37264             }
37265             while(row >= 0){
37266                 if(!first){
37267                     col = clen-1;
37268                 }
37269                 first = false;
37270                 while(col >= 0){
37271                     if(fn.call(scope || this, row, col, cm) === true){
37272                         return [row, col];
37273                     }
37274                     col--;
37275                 }
37276                 row--;
37277             }
37278         } else {
37279             if(col >= clen){
37280                 row++;
37281                 first = false;
37282             }
37283             while(row < rlen){
37284                 if(!first){
37285                     col = 0;
37286                 }
37287                 first = false;
37288                 while(col < clen){
37289                     if(fn.call(scope || this, row, col, cm) === true){
37290                         return [row, col];
37291                     }
37292                     col++;
37293                 }
37294                 row++;
37295             }
37296         }
37297         return null;
37298     },
37299
37300     // private
37301     getSelections : function(){
37302         return this.selModel.getSelections();
37303     },
37304
37305     /**
37306      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37307      * but if manual update is required this method will initiate it.
37308      */
37309     autoSize : function(){
37310         if(this.rendered){
37311             this.view.layout();
37312             if(this.view.adjustForScroll){
37313                 this.view.adjustForScroll();
37314             }
37315         }
37316     },
37317
37318     /**
37319      * Returns the grid's underlying element.
37320      * @return {Element} The element
37321      */
37322     getGridEl : function(){
37323         return this.container;
37324     },
37325
37326     // private for compatibility, overridden by editor grid
37327     stopEditing : function(){},
37328
37329     /**
37330      * Returns the grid's SelectionModel.
37331      * @return {SelectionModel}
37332      */
37333     getSelectionModel : function(){
37334         if(!this.selModel){
37335             this.selModel = new Roo.grid.RowSelectionModel();
37336         }
37337         return this.selModel;
37338     },
37339
37340     /**
37341      * Returns the grid's DataSource.
37342      * @return {DataSource}
37343      */
37344     getDataSource : function(){
37345         return this.dataSource;
37346     },
37347
37348     /**
37349      * Returns the grid's ColumnModel.
37350      * @return {ColumnModel}
37351      */
37352     getColumnModel : function(){
37353         return this.colModel;
37354     },
37355
37356     /**
37357      * Returns the grid's GridView object.
37358      * @return {GridView}
37359      */
37360     getView : function(){
37361         if(!this.view){
37362             this.view = new Roo.grid.GridView(this.viewConfig);
37363             this.relayEvents(this.view, [
37364                 "beforerowremoved", "beforerowsinserted",
37365                 "beforerefresh", "rowremoved",
37366                 "rowsinserted", "rowupdated" ,"refresh"
37367             ]);
37368         }
37369         return this.view;
37370     },
37371     /**
37372      * Called to get grid's drag proxy text, by default returns this.ddText.
37373      * Override this to put something different in the dragged text.
37374      * @return {String}
37375      */
37376     getDragDropText : function(){
37377         var count = this.selModel.getCount();
37378         return String.format(this.ddText, count, count == 1 ? '' : 's');
37379     }
37380 });
37381 /*
37382  * Based on:
37383  * Ext JS Library 1.1.1
37384  * Copyright(c) 2006-2007, Ext JS, LLC.
37385  *
37386  * Originally Released Under LGPL - original licence link has changed is not relivant.
37387  *
37388  * Fork - LGPL
37389  * <script type="text/javascript">
37390  */
37391  /**
37392  * @class Roo.grid.AbstractGridView
37393  * @extends Roo.util.Observable
37394  * @abstract
37395  * Abstract base class for grid Views
37396  * @constructor
37397  */
37398 Roo.grid.AbstractGridView = function(){
37399         this.grid = null;
37400         
37401         this.events = {
37402             "beforerowremoved" : true,
37403             "beforerowsinserted" : true,
37404             "beforerefresh" : true,
37405             "rowremoved" : true,
37406             "rowsinserted" : true,
37407             "rowupdated" : true,
37408             "refresh" : true
37409         };
37410     Roo.grid.AbstractGridView.superclass.constructor.call(this);
37411 };
37412
37413 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37414     rowClass : "x-grid-row",
37415     cellClass : "x-grid-cell",
37416     tdClass : "x-grid-td",
37417     hdClass : "x-grid-hd",
37418     splitClass : "x-grid-hd-split",
37419     
37420     init: function(grid){
37421         this.grid = grid;
37422                 var cid = this.grid.getGridEl().id;
37423         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37424         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37425         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37426         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37427         },
37428         
37429     getColumnRenderers : function(){
37430         var renderers = [];
37431         var cm = this.grid.colModel;
37432         var colCount = cm.getColumnCount();
37433         for(var i = 0; i < colCount; i++){
37434             renderers[i] = cm.getRenderer(i);
37435         }
37436         return renderers;
37437     },
37438     
37439     getColumnIds : function(){
37440         var ids = [];
37441         var cm = this.grid.colModel;
37442         var colCount = cm.getColumnCount();
37443         for(var i = 0; i < colCount; i++){
37444             ids[i] = cm.getColumnId(i);
37445         }
37446         return ids;
37447     },
37448     
37449     getDataIndexes : function(){
37450         if(!this.indexMap){
37451             this.indexMap = this.buildIndexMap();
37452         }
37453         return this.indexMap.colToData;
37454     },
37455     
37456     getColumnIndexByDataIndex : function(dataIndex){
37457         if(!this.indexMap){
37458             this.indexMap = this.buildIndexMap();
37459         }
37460         return this.indexMap.dataToCol[dataIndex];
37461     },
37462     
37463     /**
37464      * Set a css style for a column dynamically. 
37465      * @param {Number} colIndex The index of the column
37466      * @param {String} name The css property name
37467      * @param {String} value The css value
37468      */
37469     setCSSStyle : function(colIndex, name, value){
37470         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37471         Roo.util.CSS.updateRule(selector, name, value);
37472     },
37473     
37474     generateRules : function(cm){
37475         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37476         Roo.util.CSS.removeStyleSheet(rulesId);
37477         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37478             var cid = cm.getColumnId(i);
37479             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37480                          this.tdSelector, cid, " {\n}\n",
37481                          this.hdSelector, cid, " {\n}\n",
37482                          this.splitSelector, cid, " {\n}\n");
37483         }
37484         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37485     }
37486 });/*
37487  * Based on:
37488  * Ext JS Library 1.1.1
37489  * Copyright(c) 2006-2007, Ext JS, LLC.
37490  *
37491  * Originally Released Under LGPL - original licence link has changed is not relivant.
37492  *
37493  * Fork - LGPL
37494  * <script type="text/javascript">
37495  */
37496
37497 // private
37498 // This is a support class used internally by the Grid components
37499 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37500     this.grid = grid;
37501     this.view = grid.getView();
37502     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37503     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37504     if(hd2){
37505         this.setHandleElId(Roo.id(hd));
37506         this.setOuterHandleElId(Roo.id(hd2));
37507     }
37508     this.scroll = false;
37509 };
37510 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37511     maxDragWidth: 120,
37512     getDragData : function(e){
37513         var t = Roo.lib.Event.getTarget(e);
37514         var h = this.view.findHeaderCell(t);
37515         if(h){
37516             return {ddel: h.firstChild, header:h};
37517         }
37518         return false;
37519     },
37520
37521     onInitDrag : function(e){
37522         this.view.headersDisabled = true;
37523         var clone = this.dragData.ddel.cloneNode(true);
37524         clone.id = Roo.id();
37525         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37526         this.proxy.update(clone);
37527         return true;
37528     },
37529
37530     afterValidDrop : function(){
37531         var v = this.view;
37532         setTimeout(function(){
37533             v.headersDisabled = false;
37534         }, 50);
37535     },
37536
37537     afterInvalidDrop : function(){
37538         var v = this.view;
37539         setTimeout(function(){
37540             v.headersDisabled = false;
37541         }, 50);
37542     }
37543 });
37544 /*
37545  * Based on:
37546  * Ext JS Library 1.1.1
37547  * Copyright(c) 2006-2007, Ext JS, LLC.
37548  *
37549  * Originally Released Under LGPL - original licence link has changed is not relivant.
37550  *
37551  * Fork - LGPL
37552  * <script type="text/javascript">
37553  */
37554 // private
37555 // This is a support class used internally by the Grid components
37556 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37557     this.grid = grid;
37558     this.view = grid.getView();
37559     // split the proxies so they don't interfere with mouse events
37560     this.proxyTop = Roo.DomHelper.append(document.body, {
37561         cls:"col-move-top", html:"&#160;"
37562     }, true);
37563     this.proxyBottom = Roo.DomHelper.append(document.body, {
37564         cls:"col-move-bottom", html:"&#160;"
37565     }, true);
37566     this.proxyTop.hide = this.proxyBottom.hide = function(){
37567         this.setLeftTop(-100,-100);
37568         this.setStyle("visibility", "hidden");
37569     };
37570     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37571     // temporarily disabled
37572     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37573     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37574 };
37575 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37576     proxyOffsets : [-4, -9],
37577     fly: Roo.Element.fly,
37578
37579     getTargetFromEvent : function(e){
37580         var t = Roo.lib.Event.getTarget(e);
37581         var cindex = this.view.findCellIndex(t);
37582         if(cindex !== false){
37583             return this.view.getHeaderCell(cindex);
37584         }
37585         return null;
37586     },
37587
37588     nextVisible : function(h){
37589         var v = this.view, cm = this.grid.colModel;
37590         h = h.nextSibling;
37591         while(h){
37592             if(!cm.isHidden(v.getCellIndex(h))){
37593                 return h;
37594             }
37595             h = h.nextSibling;
37596         }
37597         return null;
37598     },
37599
37600     prevVisible : function(h){
37601         var v = this.view, cm = this.grid.colModel;
37602         h = h.prevSibling;
37603         while(h){
37604             if(!cm.isHidden(v.getCellIndex(h))){
37605                 return h;
37606             }
37607             h = h.prevSibling;
37608         }
37609         return null;
37610     },
37611
37612     positionIndicator : function(h, n, e){
37613         var x = Roo.lib.Event.getPageX(e);
37614         var r = Roo.lib.Dom.getRegion(n.firstChild);
37615         var px, pt, py = r.top + this.proxyOffsets[1];
37616         if((r.right - x) <= (r.right-r.left)/2){
37617             px = r.right+this.view.borderWidth;
37618             pt = "after";
37619         }else{
37620             px = r.left;
37621             pt = "before";
37622         }
37623         var oldIndex = this.view.getCellIndex(h);
37624         var newIndex = this.view.getCellIndex(n);
37625
37626         if(this.grid.colModel.isFixed(newIndex)){
37627             return false;
37628         }
37629
37630         var locked = this.grid.colModel.isLocked(newIndex);
37631
37632         if(pt == "after"){
37633             newIndex++;
37634         }
37635         if(oldIndex < newIndex){
37636             newIndex--;
37637         }
37638         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37639             return false;
37640         }
37641         px +=  this.proxyOffsets[0];
37642         this.proxyTop.setLeftTop(px, py);
37643         this.proxyTop.show();
37644         if(!this.bottomOffset){
37645             this.bottomOffset = this.view.mainHd.getHeight();
37646         }
37647         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37648         this.proxyBottom.show();
37649         return pt;
37650     },
37651
37652     onNodeEnter : function(n, dd, e, data){
37653         if(data.header != n){
37654             this.positionIndicator(data.header, n, e);
37655         }
37656     },
37657
37658     onNodeOver : function(n, dd, e, data){
37659         var result = false;
37660         if(data.header != n){
37661             result = this.positionIndicator(data.header, n, e);
37662         }
37663         if(!result){
37664             this.proxyTop.hide();
37665             this.proxyBottom.hide();
37666         }
37667         return result ? this.dropAllowed : this.dropNotAllowed;
37668     },
37669
37670     onNodeOut : function(n, dd, e, data){
37671         this.proxyTop.hide();
37672         this.proxyBottom.hide();
37673     },
37674
37675     onNodeDrop : function(n, dd, e, data){
37676         var h = data.header;
37677         if(h != n){
37678             var cm = this.grid.colModel;
37679             var x = Roo.lib.Event.getPageX(e);
37680             var r = Roo.lib.Dom.getRegion(n.firstChild);
37681             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37682             var oldIndex = this.view.getCellIndex(h);
37683             var newIndex = this.view.getCellIndex(n);
37684             var locked = cm.isLocked(newIndex);
37685             if(pt == "after"){
37686                 newIndex++;
37687             }
37688             if(oldIndex < newIndex){
37689                 newIndex--;
37690             }
37691             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37692                 return false;
37693             }
37694             cm.setLocked(oldIndex, locked, true);
37695             cm.moveColumn(oldIndex, newIndex);
37696             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37697             return true;
37698         }
37699         return false;
37700     }
37701 });
37702 /*
37703  * Based on:
37704  * Ext JS Library 1.1.1
37705  * Copyright(c) 2006-2007, Ext JS, LLC.
37706  *
37707  * Originally Released Under LGPL - original licence link has changed is not relivant.
37708  *
37709  * Fork - LGPL
37710  * <script type="text/javascript">
37711  */
37712   
37713 /**
37714  * @class Roo.grid.GridView
37715  * @extends Roo.util.Observable
37716  *
37717  * @constructor
37718  * @param {Object} config
37719  */
37720 Roo.grid.GridView = function(config){
37721     Roo.grid.GridView.superclass.constructor.call(this);
37722     this.el = null;
37723
37724     Roo.apply(this, config);
37725 };
37726
37727 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37728
37729     unselectable :  'unselectable="on"',
37730     unselectableCls :  'x-unselectable',
37731     
37732     
37733     rowClass : "x-grid-row",
37734
37735     cellClass : "x-grid-col",
37736
37737     tdClass : "x-grid-td",
37738
37739     hdClass : "x-grid-hd",
37740
37741     splitClass : "x-grid-split",
37742
37743     sortClasses : ["sort-asc", "sort-desc"],
37744
37745     enableMoveAnim : false,
37746
37747     hlColor: "C3DAF9",
37748
37749     dh : Roo.DomHelper,
37750
37751     fly : Roo.Element.fly,
37752
37753     css : Roo.util.CSS,
37754
37755     borderWidth: 1,
37756
37757     splitOffset: 3,
37758
37759     scrollIncrement : 22,
37760
37761     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37762
37763     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37764
37765     bind : function(ds, cm){
37766         if(this.ds){
37767             this.ds.un("load", this.onLoad, this);
37768             this.ds.un("datachanged", this.onDataChange, this);
37769             this.ds.un("add", this.onAdd, this);
37770             this.ds.un("remove", this.onRemove, this);
37771             this.ds.un("update", this.onUpdate, this);
37772             this.ds.un("clear", this.onClear, this);
37773         }
37774         if(ds){
37775             ds.on("load", this.onLoad, this);
37776             ds.on("datachanged", this.onDataChange, this);
37777             ds.on("add", this.onAdd, this);
37778             ds.on("remove", this.onRemove, this);
37779             ds.on("update", this.onUpdate, this);
37780             ds.on("clear", this.onClear, this);
37781         }
37782         this.ds = ds;
37783
37784         if(this.cm){
37785             this.cm.un("widthchange", this.onColWidthChange, this);
37786             this.cm.un("headerchange", this.onHeaderChange, this);
37787             this.cm.un("hiddenchange", this.onHiddenChange, this);
37788             this.cm.un("columnmoved", this.onColumnMove, this);
37789             this.cm.un("columnlockchange", this.onColumnLock, this);
37790         }
37791         if(cm){
37792             this.generateRules(cm);
37793             cm.on("widthchange", this.onColWidthChange, this);
37794             cm.on("headerchange", this.onHeaderChange, this);
37795             cm.on("hiddenchange", this.onHiddenChange, this);
37796             cm.on("columnmoved", this.onColumnMove, this);
37797             cm.on("columnlockchange", this.onColumnLock, this);
37798         }
37799         this.cm = cm;
37800     },
37801
37802     init: function(grid){
37803         Roo.grid.GridView.superclass.init.call(this, grid);
37804
37805         this.bind(grid.dataSource, grid.colModel);
37806
37807         grid.on("headerclick", this.handleHeaderClick, this);
37808
37809         if(grid.trackMouseOver){
37810             grid.on("mouseover", this.onRowOver, this);
37811             grid.on("mouseout", this.onRowOut, this);
37812         }
37813         grid.cancelTextSelection = function(){};
37814         this.gridId = grid.id;
37815
37816         var tpls = this.templates || {};
37817
37818         if(!tpls.master){
37819             tpls.master = new Roo.Template(
37820                '<div class="x-grid" hidefocus="true">',
37821                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37822                   '<div class="x-grid-topbar"></div>',
37823                   '<div class="x-grid-scroller"><div></div></div>',
37824                   '<div class="x-grid-locked">',
37825                       '<div class="x-grid-header">{lockedHeader}</div>',
37826                       '<div class="x-grid-body">{lockedBody}</div>',
37827                   "</div>",
37828                   '<div class="x-grid-viewport">',
37829                       '<div class="x-grid-header">{header}</div>',
37830                       '<div class="x-grid-body">{body}</div>',
37831                   "</div>",
37832                   '<div class="x-grid-bottombar"></div>',
37833                  
37834                   '<div class="x-grid-resize-proxy">&#160;</div>',
37835                "</div>"
37836             );
37837             tpls.master.disableformats = true;
37838         }
37839
37840         if(!tpls.header){
37841             tpls.header = new Roo.Template(
37842                '<table border="0" cellspacing="0" cellpadding="0">',
37843                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37844                "</table>{splits}"
37845             );
37846             tpls.header.disableformats = true;
37847         }
37848         tpls.header.compile();
37849
37850         if(!tpls.hcell){
37851             tpls.hcell = new Roo.Template(
37852                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37853                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37854                 "</div></td>"
37855              );
37856              tpls.hcell.disableFormats = true;
37857         }
37858         tpls.hcell.compile();
37859
37860         if(!tpls.hsplit){
37861             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37862                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37863             tpls.hsplit.disableFormats = true;
37864         }
37865         tpls.hsplit.compile();
37866
37867         if(!tpls.body){
37868             tpls.body = new Roo.Template(
37869                '<table border="0" cellspacing="0" cellpadding="0">',
37870                "<tbody>{rows}</tbody>",
37871                "</table>"
37872             );
37873             tpls.body.disableFormats = true;
37874         }
37875         tpls.body.compile();
37876
37877         if(!tpls.row){
37878             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37879             tpls.row.disableFormats = true;
37880         }
37881         tpls.row.compile();
37882
37883         if(!tpls.cell){
37884             tpls.cell = new Roo.Template(
37885                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37886                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37887                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37888                 "</td>"
37889             );
37890             tpls.cell.disableFormats = true;
37891         }
37892         tpls.cell.compile();
37893
37894         this.templates = tpls;
37895     },
37896
37897     // remap these for backwards compat
37898     onColWidthChange : function(){
37899         this.updateColumns.apply(this, arguments);
37900     },
37901     onHeaderChange : function(){
37902         this.updateHeaders.apply(this, arguments);
37903     }, 
37904     onHiddenChange : function(){
37905         this.handleHiddenChange.apply(this, arguments);
37906     },
37907     onColumnMove : function(){
37908         this.handleColumnMove.apply(this, arguments);
37909     },
37910     onColumnLock : function(){
37911         this.handleLockChange.apply(this, arguments);
37912     },
37913
37914     onDataChange : function(){
37915         this.refresh();
37916         this.updateHeaderSortState();
37917     },
37918
37919     onClear : function(){
37920         this.refresh();
37921     },
37922
37923     onUpdate : function(ds, record){
37924         this.refreshRow(record);
37925     },
37926
37927     refreshRow : function(record){
37928         var ds = this.ds, index;
37929         if(typeof record == 'number'){
37930             index = record;
37931             record = ds.getAt(index);
37932         }else{
37933             index = ds.indexOf(record);
37934         }
37935         this.insertRows(ds, index, index, true);
37936         this.onRemove(ds, record, index+1, true);
37937         this.syncRowHeights(index, index);
37938         this.layout();
37939         this.fireEvent("rowupdated", this, index, record);
37940     },
37941
37942     onAdd : function(ds, records, index){
37943         this.insertRows(ds, index, index + (records.length-1));
37944     },
37945
37946     onRemove : function(ds, record, index, isUpdate){
37947         if(isUpdate !== true){
37948             this.fireEvent("beforerowremoved", this, index, record);
37949         }
37950         var bt = this.getBodyTable(), lt = this.getLockedTable();
37951         if(bt.rows[index]){
37952             bt.firstChild.removeChild(bt.rows[index]);
37953         }
37954         if(lt.rows[index]){
37955             lt.firstChild.removeChild(lt.rows[index]);
37956         }
37957         if(isUpdate !== true){
37958             this.stripeRows(index);
37959             this.syncRowHeights(index, index);
37960             this.layout();
37961             this.fireEvent("rowremoved", this, index, record);
37962         }
37963     },
37964
37965     onLoad : function(){
37966         this.scrollToTop();
37967     },
37968
37969     /**
37970      * Scrolls the grid to the top
37971      */
37972     scrollToTop : function(){
37973         if(this.scroller){
37974             this.scroller.dom.scrollTop = 0;
37975             this.syncScroll();
37976         }
37977     },
37978
37979     /**
37980      * Gets a panel in the header of the grid that can be used for toolbars etc.
37981      * After modifying the contents of this panel a call to grid.autoSize() may be
37982      * required to register any changes in size.
37983      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37984      * @return Roo.Element
37985      */
37986     getHeaderPanel : function(doShow){
37987         if(doShow){
37988             this.headerPanel.show();
37989         }
37990         return this.headerPanel;
37991     },
37992
37993     /**
37994      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37995      * After modifying the contents of this panel a call to grid.autoSize() may be
37996      * required to register any changes in size.
37997      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37998      * @return Roo.Element
37999      */
38000     getFooterPanel : function(doShow){
38001         if(doShow){
38002             this.footerPanel.show();
38003         }
38004         return this.footerPanel;
38005     },
38006
38007     initElements : function(){
38008         var E = Roo.Element;
38009         var el = this.grid.getGridEl().dom.firstChild;
38010         var cs = el.childNodes;
38011
38012         this.el = new E(el);
38013         
38014          this.focusEl = new E(el.firstChild);
38015         this.focusEl.swallowEvent("click", true);
38016         
38017         this.headerPanel = new E(cs[1]);
38018         this.headerPanel.enableDisplayMode("block");
38019
38020         this.scroller = new E(cs[2]);
38021         this.scrollSizer = new E(this.scroller.dom.firstChild);
38022
38023         this.lockedWrap = new E(cs[3]);
38024         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
38025         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
38026
38027         this.mainWrap = new E(cs[4]);
38028         this.mainHd = new E(this.mainWrap.dom.firstChild);
38029         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
38030
38031         this.footerPanel = new E(cs[5]);
38032         this.footerPanel.enableDisplayMode("block");
38033
38034         this.resizeProxy = new E(cs[6]);
38035
38036         this.headerSelector = String.format(
38037            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38038            this.lockedHd.id, this.mainHd.id
38039         );
38040
38041         this.splitterSelector = String.format(
38042            '#{0} div.x-grid-split, #{1} div.x-grid-split',
38043            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38044         );
38045     },
38046     idToCssName : function(s)
38047     {
38048         return s.replace(/[^a-z0-9]+/ig, '-');
38049     },
38050
38051     getHeaderCell : function(index){
38052         return Roo.DomQuery.select(this.headerSelector)[index];
38053     },
38054
38055     getHeaderCellMeasure : function(index){
38056         return this.getHeaderCell(index).firstChild;
38057     },
38058
38059     getHeaderCellText : function(index){
38060         return this.getHeaderCell(index).firstChild.firstChild;
38061     },
38062
38063     getLockedTable : function(){
38064         return this.lockedBody.dom.firstChild;
38065     },
38066
38067     getBodyTable : function(){
38068         return this.mainBody.dom.firstChild;
38069     },
38070
38071     getLockedRow : function(index){
38072         return this.getLockedTable().rows[index];
38073     },
38074
38075     getRow : function(index){
38076         return this.getBodyTable().rows[index];
38077     },
38078
38079     getRowComposite : function(index){
38080         if(!this.rowEl){
38081             this.rowEl = new Roo.CompositeElementLite();
38082         }
38083         var els = [], lrow, mrow;
38084         if(lrow = this.getLockedRow(index)){
38085             els.push(lrow);
38086         }
38087         if(mrow = this.getRow(index)){
38088             els.push(mrow);
38089         }
38090         this.rowEl.elements = els;
38091         return this.rowEl;
38092     },
38093     /**
38094      * Gets the 'td' of the cell
38095      * 
38096      * @param {Integer} rowIndex row to select
38097      * @param {Integer} colIndex column to select
38098      * 
38099      * @return {Object} 
38100      */
38101     getCell : function(rowIndex, colIndex){
38102         var locked = this.cm.getLockedCount();
38103         var source;
38104         if(colIndex < locked){
38105             source = this.lockedBody.dom.firstChild;
38106         }else{
38107             source = this.mainBody.dom.firstChild;
38108             colIndex -= locked;
38109         }
38110         return source.rows[rowIndex].childNodes[colIndex];
38111     },
38112
38113     getCellText : function(rowIndex, colIndex){
38114         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38115     },
38116
38117     getCellBox : function(cell){
38118         var b = this.fly(cell).getBox();
38119         if(Roo.isOpera){ // opera fails to report the Y
38120             b.y = cell.offsetTop + this.mainBody.getY();
38121         }
38122         return b;
38123     },
38124
38125     getCellIndex : function(cell){
38126         var id = String(cell.className).match(this.cellRE);
38127         if(id){
38128             return parseInt(id[1], 10);
38129         }
38130         return 0;
38131     },
38132
38133     findHeaderIndex : function(n){
38134         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38135         return r ? this.getCellIndex(r) : false;
38136     },
38137
38138     findHeaderCell : function(n){
38139         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38140         return r ? r : false;
38141     },
38142
38143     findRowIndex : function(n){
38144         if(!n){
38145             return false;
38146         }
38147         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38148         return r ? r.rowIndex : false;
38149     },
38150
38151     findCellIndex : function(node){
38152         var stop = this.el.dom;
38153         while(node && node != stop){
38154             if(this.findRE.test(node.className)){
38155                 return this.getCellIndex(node);
38156             }
38157             node = node.parentNode;
38158         }
38159         return false;
38160     },
38161
38162     getColumnId : function(index){
38163         return this.cm.getColumnId(index);
38164     },
38165
38166     getSplitters : function()
38167     {
38168         if(this.splitterSelector){
38169            return Roo.DomQuery.select(this.splitterSelector);
38170         }else{
38171             return null;
38172       }
38173     },
38174
38175     getSplitter : function(index){
38176         return this.getSplitters()[index];
38177     },
38178
38179     onRowOver : function(e, t){
38180         var row;
38181         if((row = this.findRowIndex(t)) !== false){
38182             this.getRowComposite(row).addClass("x-grid-row-over");
38183         }
38184     },
38185
38186     onRowOut : function(e, t){
38187         var row;
38188         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38189             this.getRowComposite(row).removeClass("x-grid-row-over");
38190         }
38191     },
38192
38193     renderHeaders : function(){
38194         var cm = this.cm;
38195         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38196         var cb = [], lb = [], sb = [], lsb = [], p = {};
38197         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38198             p.cellId = "x-grid-hd-0-" + i;
38199             p.splitId = "x-grid-csplit-0-" + i;
38200             p.id = cm.getColumnId(i);
38201             p.value = cm.getColumnHeader(i) || "";
38202             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
38203             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38204             if(!cm.isLocked(i)){
38205                 cb[cb.length] = ct.apply(p);
38206                 sb[sb.length] = st.apply(p);
38207             }else{
38208                 lb[lb.length] = ct.apply(p);
38209                 lsb[lsb.length] = st.apply(p);
38210             }
38211         }
38212         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38213                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38214     },
38215
38216     updateHeaders : function(){
38217         var html = this.renderHeaders();
38218         this.lockedHd.update(html[0]);
38219         this.mainHd.update(html[1]);
38220     },
38221
38222     /**
38223      * Focuses the specified row.
38224      * @param {Number} row The row index
38225      */
38226     focusRow : function(row)
38227     {
38228         //Roo.log('GridView.focusRow');
38229         var x = this.scroller.dom.scrollLeft;
38230         this.focusCell(row, 0, false);
38231         this.scroller.dom.scrollLeft = x;
38232     },
38233
38234     /**
38235      * Focuses the specified cell.
38236      * @param {Number} row The row index
38237      * @param {Number} col The column index
38238      * @param {Boolean} hscroll false to disable horizontal scrolling
38239      */
38240     focusCell : function(row, col, hscroll)
38241     {
38242         //Roo.log('GridView.focusCell');
38243         var el = this.ensureVisible(row, col, hscroll);
38244         this.focusEl.alignTo(el, "tl-tl");
38245         if(Roo.isGecko){
38246             this.focusEl.focus();
38247         }else{
38248             this.focusEl.focus.defer(1, this.focusEl);
38249         }
38250     },
38251
38252     /**
38253      * Scrolls the specified cell into view
38254      * @param {Number} row The row index
38255      * @param {Number} col The column index
38256      * @param {Boolean} hscroll false to disable horizontal scrolling
38257      */
38258     ensureVisible : function(row, col, hscroll)
38259     {
38260         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38261         //return null; //disable for testing.
38262         if(typeof row != "number"){
38263             row = row.rowIndex;
38264         }
38265         if(row < 0 && row >= this.ds.getCount()){
38266             return  null;
38267         }
38268         col = (col !== undefined ? col : 0);
38269         var cm = this.grid.colModel;
38270         while(cm.isHidden(col)){
38271             col++;
38272         }
38273
38274         var el = this.getCell(row, col);
38275         if(!el){
38276             return null;
38277         }
38278         var c = this.scroller.dom;
38279
38280         var ctop = parseInt(el.offsetTop, 10);
38281         var cleft = parseInt(el.offsetLeft, 10);
38282         var cbot = ctop + el.offsetHeight;
38283         var cright = cleft + el.offsetWidth;
38284         
38285         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38286         var stop = parseInt(c.scrollTop, 10);
38287         var sleft = parseInt(c.scrollLeft, 10);
38288         var sbot = stop + ch;
38289         var sright = sleft + c.clientWidth;
38290         /*
38291         Roo.log('GridView.ensureVisible:' +
38292                 ' ctop:' + ctop +
38293                 ' c.clientHeight:' + c.clientHeight +
38294                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38295                 ' stop:' + stop +
38296                 ' cbot:' + cbot +
38297                 ' sbot:' + sbot +
38298                 ' ch:' + ch  
38299                 );
38300         */
38301         if(ctop < stop){
38302             c.scrollTop = ctop;
38303             //Roo.log("set scrolltop to ctop DISABLE?");
38304         }else if(cbot > sbot){
38305             //Roo.log("set scrolltop to cbot-ch");
38306             c.scrollTop = cbot-ch;
38307         }
38308         
38309         if(hscroll !== false){
38310             if(cleft < sleft){
38311                 c.scrollLeft = cleft;
38312             }else if(cright > sright){
38313                 c.scrollLeft = cright-c.clientWidth;
38314             }
38315         }
38316          
38317         return el;
38318     },
38319
38320     updateColumns : function(){
38321         this.grid.stopEditing();
38322         var cm = this.grid.colModel, colIds = this.getColumnIds();
38323         //var totalWidth = cm.getTotalWidth();
38324         var pos = 0;
38325         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38326             //if(cm.isHidden(i)) continue;
38327             var w = cm.getColumnWidth(i);
38328             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38329             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38330         }
38331         this.updateSplitters();
38332     },
38333
38334     generateRules : function(cm){
38335         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38336         Roo.util.CSS.removeStyleSheet(rulesId);
38337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38338             var cid = cm.getColumnId(i);
38339             var align = '';
38340             if(cm.config[i].align){
38341                 align = 'text-align:'+cm.config[i].align+';';
38342             }
38343             var hidden = '';
38344             if(cm.isHidden(i)){
38345                 hidden = 'display:none;';
38346             }
38347             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38348             ruleBuf.push(
38349                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38350                     this.hdSelector, cid, " {\n", align, width, "}\n",
38351                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
38352                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
38353         }
38354         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38355     },
38356
38357     updateSplitters : function(){
38358         var cm = this.cm, s = this.getSplitters();
38359         if(s){ // splitters not created yet
38360             var pos = 0, locked = true;
38361             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38362                 if(cm.isHidden(i)) {
38363                     continue;
38364                 }
38365                 var w = cm.getColumnWidth(i); // make sure it's a number
38366                 if(!cm.isLocked(i) && locked){
38367                     pos = 0;
38368                     locked = false;
38369                 }
38370                 pos += w;
38371                 s[i].style.left = (pos-this.splitOffset) + "px";
38372             }
38373         }
38374     },
38375
38376     handleHiddenChange : function(colModel, colIndex, hidden){
38377         if(hidden){
38378             this.hideColumn(colIndex);
38379         }else{
38380             this.unhideColumn(colIndex);
38381         }
38382     },
38383
38384     hideColumn : function(colIndex){
38385         var cid = this.getColumnId(colIndex);
38386         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38387         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38388         if(Roo.isSafari){
38389             this.updateHeaders();
38390         }
38391         this.updateSplitters();
38392         this.layout();
38393     },
38394
38395     unhideColumn : function(colIndex){
38396         var cid = this.getColumnId(colIndex);
38397         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38398         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38399
38400         if(Roo.isSafari){
38401             this.updateHeaders();
38402         }
38403         this.updateSplitters();
38404         this.layout();
38405     },
38406
38407     insertRows : function(dm, firstRow, lastRow, isUpdate){
38408         if(firstRow == 0 && lastRow == dm.getCount()-1){
38409             this.refresh();
38410         }else{
38411             if(!isUpdate){
38412                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38413             }
38414             var s = this.getScrollState();
38415             var markup = this.renderRows(firstRow, lastRow);
38416             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38417             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38418             this.restoreScroll(s);
38419             if(!isUpdate){
38420                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38421                 this.syncRowHeights(firstRow, lastRow);
38422                 this.stripeRows(firstRow);
38423                 this.layout();
38424             }
38425         }
38426     },
38427
38428     bufferRows : function(markup, target, index){
38429         var before = null, trows = target.rows, tbody = target.tBodies[0];
38430         if(index < trows.length){
38431             before = trows[index];
38432         }
38433         var b = document.createElement("div");
38434         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38435         var rows = b.firstChild.rows;
38436         for(var i = 0, len = rows.length; i < len; i++){
38437             if(before){
38438                 tbody.insertBefore(rows[0], before);
38439             }else{
38440                 tbody.appendChild(rows[0]);
38441             }
38442         }
38443         b.innerHTML = "";
38444         b = null;
38445     },
38446
38447     deleteRows : function(dm, firstRow, lastRow){
38448         if(dm.getRowCount()<1){
38449             this.fireEvent("beforerefresh", this);
38450             this.mainBody.update("");
38451             this.lockedBody.update("");
38452             this.fireEvent("refresh", this);
38453         }else{
38454             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38455             var bt = this.getBodyTable();
38456             var tbody = bt.firstChild;
38457             var rows = bt.rows;
38458             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38459                 tbody.removeChild(rows[firstRow]);
38460             }
38461             this.stripeRows(firstRow);
38462             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38463         }
38464     },
38465
38466     updateRows : function(dataSource, firstRow, lastRow){
38467         var s = this.getScrollState();
38468         this.refresh();
38469         this.restoreScroll(s);
38470     },
38471
38472     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38473         if(!noRefresh){
38474            this.refresh();
38475         }
38476         this.updateHeaderSortState();
38477     },
38478
38479     getScrollState : function(){
38480         
38481         var sb = this.scroller.dom;
38482         return {left: sb.scrollLeft, top: sb.scrollTop};
38483     },
38484
38485     stripeRows : function(startRow){
38486         if(!this.grid.stripeRows || this.ds.getCount() < 1){
38487             return;
38488         }
38489         startRow = startRow || 0;
38490         var rows = this.getBodyTable().rows;
38491         var lrows = this.getLockedTable().rows;
38492         var cls = ' x-grid-row-alt ';
38493         for(var i = startRow, len = rows.length; i < len; i++){
38494             var row = rows[i], lrow = lrows[i];
38495             var isAlt = ((i+1) % 2 == 0);
38496             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38497             if(isAlt == hasAlt){
38498                 continue;
38499             }
38500             if(isAlt){
38501                 row.className += " x-grid-row-alt";
38502             }else{
38503                 row.className = row.className.replace("x-grid-row-alt", "");
38504             }
38505             if(lrow){
38506                 lrow.className = row.className;
38507             }
38508         }
38509     },
38510
38511     restoreScroll : function(state){
38512         //Roo.log('GridView.restoreScroll');
38513         var sb = this.scroller.dom;
38514         sb.scrollLeft = state.left;
38515         sb.scrollTop = state.top;
38516         this.syncScroll();
38517     },
38518
38519     syncScroll : function(){
38520         //Roo.log('GridView.syncScroll');
38521         var sb = this.scroller.dom;
38522         var sh = this.mainHd.dom;
38523         var bs = this.mainBody.dom;
38524         var lv = this.lockedBody.dom;
38525         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38526         lv.scrollTop = bs.scrollTop = sb.scrollTop;
38527     },
38528
38529     handleScroll : function(e){
38530         this.syncScroll();
38531         var sb = this.scroller.dom;
38532         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38533         e.stopEvent();
38534     },
38535
38536     handleWheel : function(e){
38537         var d = e.getWheelDelta();
38538         this.scroller.dom.scrollTop -= d*22;
38539         // set this here to prevent jumpy scrolling on large tables
38540         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38541         e.stopEvent();
38542     },
38543
38544     renderRows : function(startRow, endRow){
38545         // pull in all the crap needed to render rows
38546         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38547         var colCount = cm.getColumnCount();
38548
38549         if(ds.getCount() < 1){
38550             return ["", ""];
38551         }
38552
38553         // build a map for all the columns
38554         var cs = [];
38555         for(var i = 0; i < colCount; i++){
38556             var name = cm.getDataIndex(i);
38557             cs[i] = {
38558                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38559                 renderer : cm.getRenderer(i),
38560                 id : cm.getColumnId(i),
38561                 locked : cm.isLocked(i),
38562                 has_editor : cm.isCellEditable(i)
38563             };
38564         }
38565
38566         startRow = startRow || 0;
38567         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38568
38569         // records to render
38570         var rs = ds.getRange(startRow, endRow);
38571
38572         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38573     },
38574
38575     // As much as I hate to duplicate code, this was branched because FireFox really hates
38576     // [].join("") on strings. The performance difference was substantial enough to
38577     // branch this function
38578     doRender : Roo.isGecko ?
38579             function(cs, rs, ds, startRow, colCount, stripe){
38580                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38581                 // buffers
38582                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38583                 
38584                 var hasListener = this.grid.hasListener('rowclass');
38585                 var rowcfg = {};
38586                 for(var j = 0, len = rs.length; j < len; j++){
38587                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38588                     for(var i = 0; i < colCount; i++){
38589                         c = cs[i];
38590                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38591                         p.id = c.id;
38592                         p.css = p.attr = "";
38593                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38594                         if(p.value == undefined || p.value === "") {
38595                             p.value = "&#160;";
38596                         }
38597                         if(c.has_editor){
38598                             p.css += ' x-grid-editable-cell';
38599                         }
38600                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38601                             p.css +=  ' x-grid-dirty-cell';
38602                         }
38603                         var markup = ct.apply(p);
38604                         if(!c.locked){
38605                             cb+= markup;
38606                         }else{
38607                             lcb+= markup;
38608                         }
38609                     }
38610                     var alt = [];
38611                     if(stripe && ((rowIndex+1) % 2 == 0)){
38612                         alt.push("x-grid-row-alt")
38613                     }
38614                     if(r.dirty){
38615                         alt.push(  " x-grid-dirty-row");
38616                     }
38617                     rp.cells = lcb;
38618                     if(this.getRowClass){
38619                         alt.push(this.getRowClass(r, rowIndex));
38620                     }
38621                     if (hasListener) {
38622                         rowcfg = {
38623                              
38624                             record: r,
38625                             rowIndex : rowIndex,
38626                             rowClass : ''
38627                         };
38628                         this.grid.fireEvent('rowclass', this, rowcfg);
38629                         alt.push(rowcfg.rowClass);
38630                     }
38631                     rp.alt = alt.join(" ");
38632                     lbuf+= rt.apply(rp);
38633                     rp.cells = cb;
38634                     buf+=  rt.apply(rp);
38635                 }
38636                 return [lbuf, buf];
38637             } :
38638             function(cs, rs, ds, startRow, colCount, stripe){
38639                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38640                 // buffers
38641                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38642                 var hasListener = this.grid.hasListener('rowclass');
38643  
38644                 var rowcfg = {};
38645                 for(var j = 0, len = rs.length; j < len; j++){
38646                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38647                     for(var i = 0; i < colCount; i++){
38648                         c = cs[i];
38649                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38650                         p.id = c.id;
38651                         p.css = p.attr = "";
38652                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38653                         if(p.value == undefined || p.value === "") {
38654                             p.value = "&#160;";
38655                         }
38656                         //Roo.log(c);
38657                          if(c.has_editor){
38658                             p.css += ' x-grid-editable-cell';
38659                         }
38660                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38661                             p.css += ' x-grid-dirty-cell' 
38662                         }
38663                         
38664                         var markup = ct.apply(p);
38665                         if(!c.locked){
38666                             cb[cb.length] = markup;
38667                         }else{
38668                             lcb[lcb.length] = markup;
38669                         }
38670                     }
38671                     var alt = [];
38672                     if(stripe && ((rowIndex+1) % 2 == 0)){
38673                         alt.push( "x-grid-row-alt");
38674                     }
38675                     if(r.dirty){
38676                         alt.push(" x-grid-dirty-row");
38677                     }
38678                     rp.cells = lcb;
38679                     if(this.getRowClass){
38680                         alt.push( this.getRowClass(r, rowIndex));
38681                     }
38682                     if (hasListener) {
38683                         rowcfg = {
38684                              
38685                             record: r,
38686                             rowIndex : rowIndex,
38687                             rowClass : ''
38688                         };
38689                         this.grid.fireEvent('rowclass', this, rowcfg);
38690                         alt.push(rowcfg.rowClass);
38691                     }
38692                     
38693                     rp.alt = alt.join(" ");
38694                     rp.cells = lcb.join("");
38695                     lbuf[lbuf.length] = rt.apply(rp);
38696                     rp.cells = cb.join("");
38697                     buf[buf.length] =  rt.apply(rp);
38698                 }
38699                 return [lbuf.join(""), buf.join("")];
38700             },
38701
38702     renderBody : function(){
38703         var markup = this.renderRows();
38704         var bt = this.templates.body;
38705         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38706     },
38707
38708     /**
38709      * Refreshes the grid
38710      * @param {Boolean} headersToo
38711      */
38712     refresh : function(headersToo){
38713         this.fireEvent("beforerefresh", this);
38714         this.grid.stopEditing();
38715         var result = this.renderBody();
38716         this.lockedBody.update(result[0]);
38717         this.mainBody.update(result[1]);
38718         if(headersToo === true){
38719             this.updateHeaders();
38720             this.updateColumns();
38721             this.updateSplitters();
38722             this.updateHeaderSortState();
38723         }
38724         this.syncRowHeights();
38725         this.layout();
38726         this.fireEvent("refresh", this);
38727     },
38728
38729     handleColumnMove : function(cm, oldIndex, newIndex){
38730         this.indexMap = null;
38731         var s = this.getScrollState();
38732         this.refresh(true);
38733         this.restoreScroll(s);
38734         this.afterMove(newIndex);
38735     },
38736
38737     afterMove : function(colIndex){
38738         if(this.enableMoveAnim && Roo.enableFx){
38739             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38740         }
38741         // if multisort - fix sortOrder, and reload..
38742         if (this.grid.dataSource.multiSort) {
38743             // the we can call sort again..
38744             var dm = this.grid.dataSource;
38745             var cm = this.grid.colModel;
38746             var so = [];
38747             for(var i = 0; i < cm.config.length; i++ ) {
38748                 
38749                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38750                     continue; // dont' bother, it's not in sort list or being set.
38751                 }
38752                 
38753                 so.push(cm.config[i].dataIndex);
38754             };
38755             dm.sortOrder = so;
38756             dm.load(dm.lastOptions);
38757             
38758             
38759         }
38760         
38761     },
38762
38763     updateCell : function(dm, rowIndex, dataIndex){
38764         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38765         if(typeof colIndex == "undefined"){ // not present in grid
38766             return;
38767         }
38768         var cm = this.grid.colModel;
38769         var cell = this.getCell(rowIndex, colIndex);
38770         var cellText = this.getCellText(rowIndex, colIndex);
38771
38772         var p = {
38773             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38774             id : cm.getColumnId(colIndex),
38775             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38776         };
38777         var renderer = cm.getRenderer(colIndex);
38778         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38779         if(typeof val == "undefined" || val === "") {
38780             val = "&#160;";
38781         }
38782         cellText.innerHTML = val;
38783         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38784         this.syncRowHeights(rowIndex, rowIndex);
38785     },
38786
38787     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38788         var maxWidth = 0;
38789         if(this.grid.autoSizeHeaders){
38790             var h = this.getHeaderCellMeasure(colIndex);
38791             maxWidth = Math.max(maxWidth, h.scrollWidth);
38792         }
38793         var tb, index;
38794         if(this.cm.isLocked(colIndex)){
38795             tb = this.getLockedTable();
38796             index = colIndex;
38797         }else{
38798             tb = this.getBodyTable();
38799             index = colIndex - this.cm.getLockedCount();
38800         }
38801         if(tb && tb.rows){
38802             var rows = tb.rows;
38803             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38804             for(var i = 0; i < stopIndex; i++){
38805                 var cell = rows[i].childNodes[index].firstChild;
38806                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38807             }
38808         }
38809         return maxWidth + /*margin for error in IE*/ 5;
38810     },
38811     /**
38812      * Autofit a column to its content.
38813      * @param {Number} colIndex
38814      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38815      */
38816      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38817          if(this.cm.isHidden(colIndex)){
38818              return; // can't calc a hidden column
38819          }
38820         if(forceMinSize){
38821             var cid = this.cm.getColumnId(colIndex);
38822             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38823            if(this.grid.autoSizeHeaders){
38824                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38825            }
38826         }
38827         var newWidth = this.calcColumnWidth(colIndex);
38828         this.cm.setColumnWidth(colIndex,
38829             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38830         if(!suppressEvent){
38831             this.grid.fireEvent("columnresize", colIndex, newWidth);
38832         }
38833     },
38834
38835     /**
38836      * Autofits all columns to their content and then expands to fit any extra space in the grid
38837      */
38838      autoSizeColumns : function(){
38839         var cm = this.grid.colModel;
38840         var colCount = cm.getColumnCount();
38841         for(var i = 0; i < colCount; i++){
38842             this.autoSizeColumn(i, true, true);
38843         }
38844         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38845             this.fitColumns();
38846         }else{
38847             this.updateColumns();
38848             this.layout();
38849         }
38850     },
38851
38852     /**
38853      * Autofits all columns to the grid's width proportionate with their current size
38854      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38855      */
38856     fitColumns : function(reserveScrollSpace){
38857         var cm = this.grid.colModel;
38858         var colCount = cm.getColumnCount();
38859         var cols = [];
38860         var width = 0;
38861         var i, w;
38862         for (i = 0; i < colCount; i++){
38863             if(!cm.isHidden(i) && !cm.isFixed(i)){
38864                 w = cm.getColumnWidth(i);
38865                 cols.push(i);
38866                 cols.push(w);
38867                 width += w;
38868             }
38869         }
38870         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38871         if(reserveScrollSpace){
38872             avail -= 17;
38873         }
38874         var frac = (avail - cm.getTotalWidth())/width;
38875         while (cols.length){
38876             w = cols.pop();
38877             i = cols.pop();
38878             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38879         }
38880         this.updateColumns();
38881         this.layout();
38882     },
38883
38884     onRowSelect : function(rowIndex){
38885         var row = this.getRowComposite(rowIndex);
38886         row.addClass("x-grid-row-selected");
38887     },
38888
38889     onRowDeselect : function(rowIndex){
38890         var row = this.getRowComposite(rowIndex);
38891         row.removeClass("x-grid-row-selected");
38892     },
38893
38894     onCellSelect : function(row, col){
38895         var cell = this.getCell(row, col);
38896         if(cell){
38897             Roo.fly(cell).addClass("x-grid-cell-selected");
38898         }
38899     },
38900
38901     onCellDeselect : function(row, col){
38902         var cell = this.getCell(row, col);
38903         if(cell){
38904             Roo.fly(cell).removeClass("x-grid-cell-selected");
38905         }
38906     },
38907
38908     updateHeaderSortState : function(){
38909         
38910         // sort state can be single { field: xxx, direction : yyy}
38911         // or   { xxx=>ASC , yyy : DESC ..... }
38912         
38913         var mstate = {};
38914         if (!this.ds.multiSort) { 
38915             var state = this.ds.getSortState();
38916             if(!state){
38917                 return;
38918             }
38919             mstate[state.field] = state.direction;
38920             // FIXME... - this is not used here.. but might be elsewhere..
38921             this.sortState = state;
38922             
38923         } else {
38924             mstate = this.ds.sortToggle;
38925         }
38926         //remove existing sort classes..
38927         
38928         var sc = this.sortClasses;
38929         var hds = this.el.select(this.headerSelector).removeClass(sc);
38930         
38931         for(var f in mstate) {
38932         
38933             var sortColumn = this.cm.findColumnIndex(f);
38934             
38935             if(sortColumn != -1){
38936                 var sortDir = mstate[f];        
38937                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38938             }
38939         }
38940         
38941          
38942         
38943     },
38944
38945
38946     handleHeaderClick : function(g, index,e){
38947         
38948         Roo.log("header click");
38949         
38950         if (Roo.isTouch) {
38951             // touch events on header are handled by context
38952             this.handleHdCtx(g,index,e);
38953             return;
38954         }
38955         
38956         
38957         if(this.headersDisabled){
38958             return;
38959         }
38960         var dm = g.dataSource, cm = g.colModel;
38961         if(!cm.isSortable(index)){
38962             return;
38963         }
38964         g.stopEditing();
38965         
38966         if (dm.multiSort) {
38967             // update the sortOrder
38968             var so = [];
38969             for(var i = 0; i < cm.config.length; i++ ) {
38970                 
38971                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38972                     continue; // dont' bother, it's not in sort list or being set.
38973                 }
38974                 
38975                 so.push(cm.config[i].dataIndex);
38976             };
38977             dm.sortOrder = so;
38978         }
38979         
38980         
38981         dm.sort(cm.getDataIndex(index));
38982     },
38983
38984
38985     destroy : function(){
38986         if(this.colMenu){
38987             this.colMenu.removeAll();
38988             Roo.menu.MenuMgr.unregister(this.colMenu);
38989             this.colMenu.getEl().remove();
38990             delete this.colMenu;
38991         }
38992         if(this.hmenu){
38993             this.hmenu.removeAll();
38994             Roo.menu.MenuMgr.unregister(this.hmenu);
38995             this.hmenu.getEl().remove();
38996             delete this.hmenu;
38997         }
38998         if(this.grid.enableColumnMove){
38999             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39000             if(dds){
39001                 for(var dd in dds){
39002                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
39003                         var elid = dds[dd].dragElId;
39004                         dds[dd].unreg();
39005                         Roo.get(elid).remove();
39006                     } else if(dds[dd].config.isTarget){
39007                         dds[dd].proxyTop.remove();
39008                         dds[dd].proxyBottom.remove();
39009                         dds[dd].unreg();
39010                     }
39011                     if(Roo.dd.DDM.locationCache[dd]){
39012                         delete Roo.dd.DDM.locationCache[dd];
39013                     }
39014                 }
39015                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39016             }
39017         }
39018         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
39019         this.bind(null, null);
39020         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
39021     },
39022
39023     handleLockChange : function(){
39024         this.refresh(true);
39025     },
39026
39027     onDenyColumnLock : function(){
39028
39029     },
39030
39031     onDenyColumnHide : function(){
39032
39033     },
39034
39035     handleHdMenuClick : function(item){
39036         var index = this.hdCtxIndex;
39037         var cm = this.cm, ds = this.ds;
39038         switch(item.id){
39039             case "asc":
39040                 ds.sort(cm.getDataIndex(index), "ASC");
39041                 break;
39042             case "desc":
39043                 ds.sort(cm.getDataIndex(index), "DESC");
39044                 break;
39045             case "lock":
39046                 var lc = cm.getLockedCount();
39047                 if(cm.getColumnCount(true) <= lc+1){
39048                     this.onDenyColumnLock();
39049                     return;
39050                 }
39051                 if(lc != index){
39052                     cm.setLocked(index, true, true);
39053                     cm.moveColumn(index, lc);
39054                     this.grid.fireEvent("columnmove", index, lc);
39055                 }else{
39056                     cm.setLocked(index, true);
39057                 }
39058             break;
39059             case "unlock":
39060                 var lc = cm.getLockedCount();
39061                 if((lc-1) != index){
39062                     cm.setLocked(index, false, true);
39063                     cm.moveColumn(index, lc-1);
39064                     this.grid.fireEvent("columnmove", index, lc-1);
39065                 }else{
39066                     cm.setLocked(index, false);
39067                 }
39068             break;
39069             case 'wider': // used to expand cols on touch..
39070             case 'narrow':
39071                 var cw = cm.getColumnWidth(index);
39072                 cw += (item.id == 'wider' ? 1 : -1) * 50;
39073                 cw = Math.max(0, cw);
39074                 cw = Math.min(cw,4000);
39075                 cm.setColumnWidth(index, cw);
39076                 break;
39077                 
39078             default:
39079                 index = cm.getIndexById(item.id.substr(4));
39080                 if(index != -1){
39081                     if(item.checked && cm.getColumnCount(true) <= 1){
39082                         this.onDenyColumnHide();
39083                         return false;
39084                     }
39085                     cm.setHidden(index, item.checked);
39086                 }
39087         }
39088         return true;
39089     },
39090
39091     beforeColMenuShow : function(){
39092         var cm = this.cm,  colCount = cm.getColumnCount();
39093         this.colMenu.removeAll();
39094         
39095         var items = [];
39096         for(var i = 0; i < colCount; i++){
39097             items.push({
39098                 id: "col-"+cm.getColumnId(i),
39099                 text: cm.getColumnHeader(i),
39100                 checked: !cm.isHidden(i),
39101                 hideOnClick:false
39102             });
39103         }
39104         
39105         if (this.grid.sortColMenu) {
39106             items.sort(function(a,b) {
39107                 if (a.text == b.text) {
39108                     return 0;
39109                 }
39110                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39111             });
39112         }
39113         
39114         for(var i = 0; i < colCount; i++){
39115             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39116         }
39117     },
39118
39119     handleHdCtx : function(g, index, e){
39120         e.stopEvent();
39121         var hd = this.getHeaderCell(index);
39122         this.hdCtxIndex = index;
39123         var ms = this.hmenu.items, cm = this.cm;
39124         ms.get("asc").setDisabled(!cm.isSortable(index));
39125         ms.get("desc").setDisabled(!cm.isSortable(index));
39126         if(this.grid.enableColLock !== false){
39127             ms.get("lock").setDisabled(cm.isLocked(index));
39128             ms.get("unlock").setDisabled(!cm.isLocked(index));
39129         }
39130         this.hmenu.show(hd, "tl-bl");
39131     },
39132
39133     handleHdOver : function(e){
39134         var hd = this.findHeaderCell(e.getTarget());
39135         if(hd && !this.headersDisabled){
39136             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39137                this.fly(hd).addClass("x-grid-hd-over");
39138             }
39139         }
39140     },
39141
39142     handleHdOut : function(e){
39143         var hd = this.findHeaderCell(e.getTarget());
39144         if(hd){
39145             this.fly(hd).removeClass("x-grid-hd-over");
39146         }
39147     },
39148
39149     handleSplitDblClick : function(e, t){
39150         var i = this.getCellIndex(t);
39151         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39152             this.autoSizeColumn(i, true);
39153             this.layout();
39154         }
39155     },
39156
39157     render : function(){
39158
39159         var cm = this.cm;
39160         var colCount = cm.getColumnCount();
39161
39162         if(this.grid.monitorWindowResize === true){
39163             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39164         }
39165         var header = this.renderHeaders();
39166         var body = this.templates.body.apply({rows:""});
39167         var html = this.templates.master.apply({
39168             lockedBody: body,
39169             body: body,
39170             lockedHeader: header[0],
39171             header: header[1]
39172         });
39173
39174         //this.updateColumns();
39175
39176         this.grid.getGridEl().dom.innerHTML = html;
39177
39178         this.initElements();
39179         
39180         // a kludge to fix the random scolling effect in webkit
39181         this.el.on("scroll", function() {
39182             this.el.dom.scrollTop=0; // hopefully not recursive..
39183         },this);
39184
39185         this.scroller.on("scroll", this.handleScroll, this);
39186         this.lockedBody.on("mousewheel", this.handleWheel, this);
39187         this.mainBody.on("mousewheel", this.handleWheel, this);
39188
39189         this.mainHd.on("mouseover", this.handleHdOver, this);
39190         this.mainHd.on("mouseout", this.handleHdOut, this);
39191         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39192                 {delegate: "."+this.splitClass});
39193
39194         this.lockedHd.on("mouseover", this.handleHdOver, this);
39195         this.lockedHd.on("mouseout", this.handleHdOut, this);
39196         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39197                 {delegate: "."+this.splitClass});
39198
39199         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39200             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39201         }
39202
39203         this.updateSplitters();
39204
39205         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39206             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39207             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39208         }
39209
39210         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39211             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39212             this.hmenu.add(
39213                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39214                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39215             );
39216             if(this.grid.enableColLock !== false){
39217                 this.hmenu.add('-',
39218                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39219                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39220                 );
39221             }
39222             if (Roo.isTouch) {
39223                  this.hmenu.add('-',
39224                     {id:"wider", text: this.columnsWiderText},
39225                     {id:"narrow", text: this.columnsNarrowText }
39226                 );
39227                 
39228                  
39229             }
39230             
39231             if(this.grid.enableColumnHide !== false){
39232
39233                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39234                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39235                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39236
39237                 this.hmenu.add('-',
39238                     {id:"columns", text: this.columnsText, menu: this.colMenu}
39239                 );
39240             }
39241             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39242
39243             this.grid.on("headercontextmenu", this.handleHdCtx, this);
39244         }
39245
39246         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39247             this.dd = new Roo.grid.GridDragZone(this.grid, {
39248                 ddGroup : this.grid.ddGroup || 'GridDD'
39249             });
39250             
39251         }
39252
39253         /*
39254         for(var i = 0; i < colCount; i++){
39255             if(cm.isHidden(i)){
39256                 this.hideColumn(i);
39257             }
39258             if(cm.config[i].align){
39259                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39260                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39261             }
39262         }*/
39263         
39264         this.updateHeaderSortState();
39265
39266         this.beforeInitialResize();
39267         this.layout(true);
39268
39269         // two part rendering gives faster view to the user
39270         this.renderPhase2.defer(1, this);
39271     },
39272
39273     renderPhase2 : function(){
39274         // render the rows now
39275         this.refresh();
39276         if(this.grid.autoSizeColumns){
39277             this.autoSizeColumns();
39278         }
39279     },
39280
39281     beforeInitialResize : function(){
39282
39283     },
39284
39285     onColumnSplitterMoved : function(i, w){
39286         this.userResized = true;
39287         var cm = this.grid.colModel;
39288         cm.setColumnWidth(i, w, true);
39289         var cid = cm.getColumnId(i);
39290         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39291         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39292         this.updateSplitters();
39293         this.layout();
39294         this.grid.fireEvent("columnresize", i, w);
39295     },
39296
39297     syncRowHeights : function(startIndex, endIndex){
39298         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39299             startIndex = startIndex || 0;
39300             var mrows = this.getBodyTable().rows;
39301             var lrows = this.getLockedTable().rows;
39302             var len = mrows.length-1;
39303             endIndex = Math.min(endIndex || len, len);
39304             for(var i = startIndex; i <= endIndex; i++){
39305                 var m = mrows[i], l = lrows[i];
39306                 var h = Math.max(m.offsetHeight, l.offsetHeight);
39307                 m.style.height = l.style.height = h + "px";
39308             }
39309         }
39310     },
39311
39312     layout : function(initialRender, is2ndPass)
39313     {
39314         var g = this.grid;
39315         var auto = g.autoHeight;
39316         var scrollOffset = 16;
39317         var c = g.getGridEl(), cm = this.cm,
39318                 expandCol = g.autoExpandColumn,
39319                 gv = this;
39320         //c.beginMeasure();
39321
39322         if(!c.dom.offsetWidth){ // display:none?
39323             if(initialRender){
39324                 this.lockedWrap.show();
39325                 this.mainWrap.show();
39326             }
39327             return;
39328         }
39329
39330         var hasLock = this.cm.isLocked(0);
39331
39332         var tbh = this.headerPanel.getHeight();
39333         var bbh = this.footerPanel.getHeight();
39334
39335         if(auto){
39336             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39337             var newHeight = ch + c.getBorderWidth("tb");
39338             if(g.maxHeight){
39339                 newHeight = Math.min(g.maxHeight, newHeight);
39340             }
39341             c.setHeight(newHeight);
39342         }
39343
39344         if(g.autoWidth){
39345             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39346         }
39347
39348         var s = this.scroller;
39349
39350         var csize = c.getSize(true);
39351
39352         this.el.setSize(csize.width, csize.height);
39353
39354         this.headerPanel.setWidth(csize.width);
39355         this.footerPanel.setWidth(csize.width);
39356
39357         var hdHeight = this.mainHd.getHeight();
39358         var vw = csize.width;
39359         var vh = csize.height - (tbh + bbh);
39360
39361         s.setSize(vw, vh);
39362
39363         var bt = this.getBodyTable();
39364         
39365         if(cm.getLockedCount() == cm.config.length){
39366             bt = this.getLockedTable();
39367         }
39368         
39369         var ltWidth = hasLock ?
39370                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39371
39372         var scrollHeight = bt.offsetHeight;
39373         var scrollWidth = ltWidth + bt.offsetWidth;
39374         var vscroll = false, hscroll = false;
39375
39376         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39377
39378         var lw = this.lockedWrap, mw = this.mainWrap;
39379         var lb = this.lockedBody, mb = this.mainBody;
39380
39381         setTimeout(function(){
39382             var t = s.dom.offsetTop;
39383             var w = s.dom.clientWidth,
39384                 h = s.dom.clientHeight;
39385
39386             lw.setTop(t);
39387             lw.setSize(ltWidth, h);
39388
39389             mw.setLeftTop(ltWidth, t);
39390             mw.setSize(w-ltWidth, h);
39391
39392             lb.setHeight(h-hdHeight);
39393             mb.setHeight(h-hdHeight);
39394
39395             if(is2ndPass !== true && !gv.userResized && expandCol){
39396                 // high speed resize without full column calculation
39397                 
39398                 var ci = cm.getIndexById(expandCol);
39399                 if (ci < 0) {
39400                     ci = cm.findColumnIndex(expandCol);
39401                 }
39402                 ci = Math.max(0, ci); // make sure it's got at least the first col.
39403                 var expandId = cm.getColumnId(ci);
39404                 var  tw = cm.getTotalWidth(false);
39405                 var currentWidth = cm.getColumnWidth(ci);
39406                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39407                 if(currentWidth != cw){
39408                     cm.setColumnWidth(ci, cw, true);
39409                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39410                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39411                     gv.updateSplitters();
39412                     gv.layout(false, true);
39413                 }
39414             }
39415
39416             if(initialRender){
39417                 lw.show();
39418                 mw.show();
39419             }
39420             //c.endMeasure();
39421         }, 10);
39422     },
39423
39424     onWindowResize : function(){
39425         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39426             return;
39427         }
39428         this.layout();
39429     },
39430
39431     appendFooter : function(parentEl){
39432         return null;
39433     },
39434
39435     sortAscText : "Sort Ascending",
39436     sortDescText : "Sort Descending",
39437     lockText : "Lock Column",
39438     unlockText : "Unlock Column",
39439     columnsText : "Columns",
39440  
39441     columnsWiderText : "Wider",
39442     columnsNarrowText : "Thinner"
39443 });
39444
39445
39446 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39447     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39448     this.proxy.el.addClass('x-grid3-col-dd');
39449 };
39450
39451 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39452     handleMouseDown : function(e){
39453
39454     },
39455
39456     callHandleMouseDown : function(e){
39457         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39458     }
39459 });
39460 /*
39461  * Based on:
39462  * Ext JS Library 1.1.1
39463  * Copyright(c) 2006-2007, Ext JS, LLC.
39464  *
39465  * Originally Released Under LGPL - original licence link has changed is not relivant.
39466  *
39467  * Fork - LGPL
39468  * <script type="text/javascript">
39469  */
39470  /**
39471  * @extends Roo.dd.DDProxy
39472  * @class Roo.grid.SplitDragZone
39473  * Support for Column Header resizing
39474  * @constructor
39475  * @param {Object} config
39476  */
39477 // private
39478 // This is a support class used internally by the Grid components
39479 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39480     this.grid = grid;
39481     this.view = grid.getView();
39482     this.proxy = this.view.resizeProxy;
39483     Roo.grid.SplitDragZone.superclass.constructor.call(
39484         this,
39485         hd, // ID
39486         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39487         {  // CONFIG
39488             dragElId : Roo.id(this.proxy.dom),
39489             resizeFrame:false
39490         }
39491     );
39492     
39493     this.setHandleElId(Roo.id(hd));
39494     if (hd2 !== false) {
39495         this.setOuterHandleElId(Roo.id(hd2));
39496     }
39497     
39498     this.scroll = false;
39499 };
39500 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39501     fly: Roo.Element.fly,
39502
39503     b4StartDrag : function(x, y){
39504         this.view.headersDisabled = true;
39505         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39506                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39507         );
39508         this.proxy.setHeight(h);
39509         
39510         // for old system colWidth really stored the actual width?
39511         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39512         // which in reality did not work.. - it worked only for fixed sizes
39513         // for resizable we need to use actual sizes.
39514         var w = this.cm.getColumnWidth(this.cellIndex);
39515         if (!this.view.mainWrap) {
39516             // bootstrap.
39517             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39518         }
39519         
39520         
39521         
39522         // this was w-this.grid.minColumnWidth;
39523         // doesnt really make sense? - w = thie curren width or the rendered one?
39524         var minw = Math.max(w-this.grid.minColumnWidth, 0);
39525         this.resetConstraints();
39526         this.setXConstraint(minw, 1000);
39527         this.setYConstraint(0, 0);
39528         this.minX = x - minw;
39529         this.maxX = x + 1000;
39530         this.startPos = x;
39531         if (!this.view.mainWrap) { // this is Bootstrap code..
39532             this.getDragEl().style.display='block';
39533         }
39534         
39535         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39536     },
39537
39538
39539     handleMouseDown : function(e){
39540         ev = Roo.EventObject.setEvent(e);
39541         var t = this.fly(ev.getTarget());
39542         if(t.hasClass("x-grid-split")){
39543             this.cellIndex = this.view.getCellIndex(t.dom);
39544             this.split = t.dom;
39545             this.cm = this.grid.colModel;
39546             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39547                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39548             }
39549         }
39550     },
39551
39552     endDrag : function(e){
39553         this.view.headersDisabled = false;
39554         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39555         var diff = endX - this.startPos;
39556         // 
39557         var w = this.cm.getColumnWidth(this.cellIndex);
39558         if (!this.view.mainWrap) {
39559             w = 0;
39560         }
39561         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39562     },
39563
39564     autoOffset : function(){
39565         this.setDelta(0,0);
39566     }
39567 });/*
39568  * Based on:
39569  * Ext JS Library 1.1.1
39570  * Copyright(c) 2006-2007, Ext JS, LLC.
39571  *
39572  * Originally Released Under LGPL - original licence link has changed is not relivant.
39573  *
39574  * Fork - LGPL
39575  * <script type="text/javascript">
39576  */
39577  
39578 // private
39579 // This is a support class used internally by the Grid components
39580 Roo.grid.GridDragZone = function(grid, config){
39581     this.view = grid.getView();
39582     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39583     if(this.view.lockedBody){
39584         this.setHandleElId(Roo.id(this.view.mainBody.dom));
39585         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39586     }
39587     this.scroll = false;
39588     this.grid = grid;
39589     this.ddel = document.createElement('div');
39590     this.ddel.className = 'x-grid-dd-wrap';
39591 };
39592
39593 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39594     ddGroup : "GridDD",
39595
39596     getDragData : function(e){
39597         var t = Roo.lib.Event.getTarget(e);
39598         var rowIndex = this.view.findRowIndex(t);
39599         var sm = this.grid.selModel;
39600             
39601         //Roo.log(rowIndex);
39602         
39603         if (sm.getSelectedCell) {
39604             // cell selection..
39605             if (!sm.getSelectedCell()) {
39606                 return false;
39607             }
39608             if (rowIndex != sm.getSelectedCell()[0]) {
39609                 return false;
39610             }
39611         
39612         }
39613         if (sm.getSelections && sm.getSelections().length < 1) {
39614             return false;
39615         }
39616         
39617         
39618         // before it used to all dragging of unseleted... - now we dont do that.
39619         if(rowIndex !== false){
39620             
39621             // if editorgrid.. 
39622             
39623             
39624             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39625                
39626             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39627               //  
39628             //}
39629             if (e.hasModifier()){
39630                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39631             }
39632             
39633             Roo.log("getDragData");
39634             
39635             return {
39636                 grid: this.grid,
39637                 ddel: this.ddel,
39638                 rowIndex: rowIndex,
39639                 selections: sm.getSelections ? sm.getSelections() : (
39640                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39641             };
39642         }
39643         return false;
39644     },
39645     
39646     
39647     onInitDrag : function(e){
39648         var data = this.dragData;
39649         this.ddel.innerHTML = this.grid.getDragDropText();
39650         this.proxy.update(this.ddel);
39651         // fire start drag?
39652     },
39653
39654     afterRepair : function(){
39655         this.dragging = false;
39656     },
39657
39658     getRepairXY : function(e, data){
39659         return false;
39660     },
39661
39662     onEndDrag : function(data, e){
39663         // fire end drag?
39664     },
39665
39666     onValidDrop : function(dd, e, id){
39667         // fire drag drop?
39668         this.hideProxy();
39669     },
39670
39671     beforeInvalidDrop : function(e, id){
39672
39673     }
39674 });/*
39675  * Based on:
39676  * Ext JS Library 1.1.1
39677  * Copyright(c) 2006-2007, Ext JS, LLC.
39678  *
39679  * Originally Released Under LGPL - original licence link has changed is not relivant.
39680  *
39681  * Fork - LGPL
39682  * <script type="text/javascript">
39683  */
39684  
39685
39686 /**
39687  * @class Roo.grid.ColumnModel
39688  * @extends Roo.util.Observable
39689  * This is the default implementation of a ColumnModel used by the Grid. It defines
39690  * the columns in the grid.
39691  * <br>Usage:<br>
39692  <pre><code>
39693  var colModel = new Roo.grid.ColumnModel([
39694         {header: "Ticker", width: 60, sortable: true, locked: true},
39695         {header: "Company Name", width: 150, sortable: true},
39696         {header: "Market Cap.", width: 100, sortable: true},
39697         {header: "$ Sales", width: 100, sortable: true, renderer: money},
39698         {header: "Employees", width: 100, sortable: true, resizable: false}
39699  ]);
39700  </code></pre>
39701  * <p>
39702  
39703  * The config options listed for this class are options which may appear in each
39704  * individual column definition.
39705  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39706  * @constructor
39707  * @param {Object} config An Array of column config objects. See this class's
39708  * config objects for details.
39709 */
39710 Roo.grid.ColumnModel = function(config){
39711         /**
39712      * The config passed into the constructor
39713      */
39714     this.config = []; //config;
39715     this.lookup = {};
39716
39717     // if no id, create one
39718     // if the column does not have a dataIndex mapping,
39719     // map it to the order it is in the config
39720     for(var i = 0, len = config.length; i < len; i++){
39721         this.addColumn(config[i]);
39722         
39723     }
39724
39725     /**
39726      * The width of columns which have no width specified (defaults to 100)
39727      * @type Number
39728      */
39729     this.defaultWidth = 100;
39730
39731     /**
39732      * Default sortable of columns which have no sortable specified (defaults to false)
39733      * @type Boolean
39734      */
39735     this.defaultSortable = false;
39736
39737     this.addEvents({
39738         /**
39739              * @event widthchange
39740              * Fires when the width of a column changes.
39741              * @param {ColumnModel} this
39742              * @param {Number} columnIndex The column index
39743              * @param {Number} newWidth The new width
39744              */
39745             "widthchange": true,
39746         /**
39747              * @event headerchange
39748              * Fires when the text of a header changes.
39749              * @param {ColumnModel} this
39750              * @param {Number} columnIndex The column index
39751              * @param {Number} newText The new header text
39752              */
39753             "headerchange": true,
39754         /**
39755              * @event hiddenchange
39756              * Fires when a column is hidden or "unhidden".
39757              * @param {ColumnModel} this
39758              * @param {Number} columnIndex The column index
39759              * @param {Boolean} hidden true if hidden, false otherwise
39760              */
39761             "hiddenchange": true,
39762             /**
39763          * @event columnmoved
39764          * Fires when a column is moved.
39765          * @param {ColumnModel} this
39766          * @param {Number} oldIndex
39767          * @param {Number} newIndex
39768          */
39769         "columnmoved" : true,
39770         /**
39771          * @event columlockchange
39772          * Fires when a column's locked state is changed
39773          * @param {ColumnModel} this
39774          * @param {Number} colIndex
39775          * @param {Boolean} locked true if locked
39776          */
39777         "columnlockchange" : true
39778     });
39779     Roo.grid.ColumnModel.superclass.constructor.call(this);
39780 };
39781 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39782     /**
39783      * @cfg {String} header The header text to display in the Grid view.
39784      */
39785         /**
39786      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
39787      */
39788         /**
39789      * @cfg {String} smHeader Header at Bootsrap Small width
39790      */
39791         /**
39792      * @cfg {String} mdHeader Header at Bootsrap Medium width
39793      */
39794         /**
39795      * @cfg {String} lgHeader Header at Bootsrap Large width
39796      */
39797         /**
39798      * @cfg {String} xlHeader Header at Bootsrap extra Large width
39799      */
39800     /**
39801      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39802      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39803      * specified, the column's index is used as an index into the Record's data Array.
39804      */
39805     /**
39806      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39807      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39808      */
39809     /**
39810      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39811      * Defaults to the value of the {@link #defaultSortable} property.
39812      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39813      */
39814     /**
39815      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39816      */
39817     /**
39818      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39819      */
39820     /**
39821      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39822      */
39823     /**
39824      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39825      */
39826     /**
39827      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39828      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39829      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
39830      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39831      */
39832        /**
39833      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39834      */
39835     /**
39836      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39837      */
39838     /**
39839      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
39840      */
39841     /**
39842      * @cfg {String} cursor (Optional)
39843      */
39844     /**
39845      * @cfg {String} tooltip (Optional)
39846      */
39847     /**
39848      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
39849      */
39850     /**
39851      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
39852      */
39853     /**
39854      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
39855      */
39856     /**
39857      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
39858      */
39859         /**
39860      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
39861      */
39862     /**
39863      * Returns the id of the column at the specified index.
39864      * @param {Number} index The column index
39865      * @return {String} the id
39866      */
39867     getColumnId : function(index){
39868         return this.config[index].id;
39869     },
39870
39871     /**
39872      * Returns the column for a specified id.
39873      * @param {String} id The column id
39874      * @return {Object} the column
39875      */
39876     getColumnById : function(id){
39877         return this.lookup[id];
39878     },
39879
39880     
39881     /**
39882      * Returns the column Object for a specified dataIndex.
39883      * @param {String} dataIndex The column dataIndex
39884      * @return {Object|Boolean} the column or false if not found
39885      */
39886     getColumnByDataIndex: function(dataIndex){
39887         var index = this.findColumnIndex(dataIndex);
39888         return index > -1 ? this.config[index] : false;
39889     },
39890     
39891     /**
39892      * Returns the index for a specified column id.
39893      * @param {String} id The column id
39894      * @return {Number} the index, or -1 if not found
39895      */
39896     getIndexById : function(id){
39897         for(var i = 0, len = this.config.length; i < len; i++){
39898             if(this.config[i].id == id){
39899                 return i;
39900             }
39901         }
39902         return -1;
39903     },
39904     
39905     /**
39906      * Returns the index for a specified column dataIndex.
39907      * @param {String} dataIndex The column dataIndex
39908      * @return {Number} the index, or -1 if not found
39909      */
39910     
39911     findColumnIndex : function(dataIndex){
39912         for(var i = 0, len = this.config.length; i < len; i++){
39913             if(this.config[i].dataIndex == dataIndex){
39914                 return i;
39915             }
39916         }
39917         return -1;
39918     },
39919     
39920     
39921     moveColumn : function(oldIndex, newIndex){
39922         var c = this.config[oldIndex];
39923         this.config.splice(oldIndex, 1);
39924         this.config.splice(newIndex, 0, c);
39925         this.dataMap = null;
39926         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39927     },
39928
39929     isLocked : function(colIndex){
39930         return this.config[colIndex].locked === true;
39931     },
39932
39933     setLocked : function(colIndex, value, suppressEvent){
39934         if(this.isLocked(colIndex) == value){
39935             return;
39936         }
39937         this.config[colIndex].locked = value;
39938         if(!suppressEvent){
39939             this.fireEvent("columnlockchange", this, colIndex, value);
39940         }
39941     },
39942
39943     getTotalLockedWidth : function(){
39944         var totalWidth = 0;
39945         for(var i = 0; i < this.config.length; i++){
39946             if(this.isLocked(i) && !this.isHidden(i)){
39947                 this.totalWidth += this.getColumnWidth(i);
39948             }
39949         }
39950         return totalWidth;
39951     },
39952
39953     getLockedCount : function(){
39954         for(var i = 0, len = this.config.length; i < len; i++){
39955             if(!this.isLocked(i)){
39956                 return i;
39957             }
39958         }
39959         
39960         return this.config.length;
39961     },
39962
39963     /**
39964      * Returns the number of columns.
39965      * @return {Number}
39966      */
39967     getColumnCount : function(visibleOnly){
39968         if(visibleOnly === true){
39969             var c = 0;
39970             for(var i = 0, len = this.config.length; i < len; i++){
39971                 if(!this.isHidden(i)){
39972                     c++;
39973                 }
39974             }
39975             return c;
39976         }
39977         return this.config.length;
39978     },
39979
39980     /**
39981      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39982      * @param {Function} fn
39983      * @param {Object} scope (optional)
39984      * @return {Array} result
39985      */
39986     getColumnsBy : function(fn, scope){
39987         var r = [];
39988         for(var i = 0, len = this.config.length; i < len; i++){
39989             var c = this.config[i];
39990             if(fn.call(scope||this, c, i) === true){
39991                 r[r.length] = c;
39992             }
39993         }
39994         return r;
39995     },
39996
39997     /**
39998      * Returns true if the specified column is sortable.
39999      * @param {Number} col The column index
40000      * @return {Boolean}
40001      */
40002     isSortable : function(col){
40003         if(typeof this.config[col].sortable == "undefined"){
40004             return this.defaultSortable;
40005         }
40006         return this.config[col].sortable;
40007     },
40008
40009     /**
40010      * Returns the rendering (formatting) function defined for the column.
40011      * @param {Number} col The column index.
40012      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
40013      */
40014     getRenderer : function(col){
40015         if(!this.config[col].renderer){
40016             return Roo.grid.ColumnModel.defaultRenderer;
40017         }
40018         return this.config[col].renderer;
40019     },
40020
40021     /**
40022      * Sets the rendering (formatting) function for a column.
40023      * @param {Number} col The column index
40024      * @param {Function} fn The function to use to process the cell's raw data
40025      * to return HTML markup for the grid view. The render function is called with
40026      * the following parameters:<ul>
40027      * <li>Data value.</li>
40028      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
40029      * <li>css A CSS style string to apply to the table cell.</li>
40030      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
40031      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
40032      * <li>Row index</li>
40033      * <li>Column index</li>
40034      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
40035      */
40036     setRenderer : function(col, fn){
40037         this.config[col].renderer = fn;
40038     },
40039
40040     /**
40041      * Returns the width for the specified column.
40042      * @param {Number} col The column index
40043      * @param (optional) {String} gridSize bootstrap width size.
40044      * @return {Number}
40045      */
40046     getColumnWidth : function(col, gridSize)
40047         {
40048                 var cfg = this.config[col];
40049                 
40050                 if (typeof(gridSize) == 'undefined') {
40051                         return cfg.width * 1 || this.defaultWidth;
40052                 }
40053                 if (gridSize === false) { // if we set it..
40054                         return cfg.width || false;
40055                 }
40056                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40057                 
40058                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40059                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40060                                 continue;
40061                         }
40062                         return cfg[ sizes[i] ];
40063                 }
40064                 return 1;
40065                 
40066     },
40067
40068     /**
40069      * Sets the width for a column.
40070      * @param {Number} col The column index
40071      * @param {Number} width The new width
40072      */
40073     setColumnWidth : function(col, width, suppressEvent){
40074         this.config[col].width = width;
40075         this.totalWidth = null;
40076         if(!suppressEvent){
40077              this.fireEvent("widthchange", this, col, width);
40078         }
40079     },
40080
40081     /**
40082      * Returns the total width of all columns.
40083      * @param {Boolean} includeHidden True to include hidden column widths
40084      * @return {Number}
40085      */
40086     getTotalWidth : function(includeHidden){
40087         if(!this.totalWidth){
40088             this.totalWidth = 0;
40089             for(var i = 0, len = this.config.length; i < len; i++){
40090                 if(includeHidden || !this.isHidden(i)){
40091                     this.totalWidth += this.getColumnWidth(i);
40092                 }
40093             }
40094         }
40095         return this.totalWidth;
40096     },
40097
40098     /**
40099      * Returns the header for the specified column.
40100      * @param {Number} col The column index
40101      * @return {String}
40102      */
40103     getColumnHeader : function(col){
40104         return this.config[col].header;
40105     },
40106
40107     /**
40108      * Sets the header for a column.
40109      * @param {Number} col The column index
40110      * @param {String} header The new header
40111      */
40112     setColumnHeader : function(col, header){
40113         this.config[col].header = header;
40114         this.fireEvent("headerchange", this, col, header);
40115     },
40116
40117     /**
40118      * Returns the tooltip for the specified column.
40119      * @param {Number} col The column index
40120      * @return {String}
40121      */
40122     getColumnTooltip : function(col){
40123             return this.config[col].tooltip;
40124     },
40125     /**
40126      * Sets the tooltip for a column.
40127      * @param {Number} col The column index
40128      * @param {String} tooltip The new tooltip
40129      */
40130     setColumnTooltip : function(col, tooltip){
40131             this.config[col].tooltip = tooltip;
40132     },
40133
40134     /**
40135      * Returns the dataIndex for the specified column.
40136      * @param {Number} col The column index
40137      * @return {Number}
40138      */
40139     getDataIndex : function(col){
40140         return this.config[col].dataIndex;
40141     },
40142
40143     /**
40144      * Sets the dataIndex for a column.
40145      * @param {Number} col The column index
40146      * @param {Number} dataIndex The new dataIndex
40147      */
40148     setDataIndex : function(col, dataIndex){
40149         this.config[col].dataIndex = dataIndex;
40150     },
40151
40152     
40153     
40154     /**
40155      * Returns true if the cell is editable.
40156      * @param {Number} colIndex The column index
40157      * @param {Number} rowIndex The row index - this is nto actually used..?
40158      * @return {Boolean}
40159      */
40160     isCellEditable : function(colIndex, rowIndex){
40161         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40162     },
40163
40164     /**
40165      * Returns the editor defined for the cell/column.
40166      * return false or null to disable editing.
40167      * @param {Number} colIndex The column index
40168      * @param {Number} rowIndex The row index
40169      * @return {Object}
40170      */
40171     getCellEditor : function(colIndex, rowIndex){
40172         return this.config[colIndex].editor;
40173     },
40174
40175     /**
40176      * Sets if a column is editable.
40177      * @param {Number} col The column index
40178      * @param {Boolean} editable True if the column is editable
40179      */
40180     setEditable : function(col, editable){
40181         this.config[col].editable = editable;
40182     },
40183
40184
40185     /**
40186      * Returns true if the column is hidden.
40187      * @param {Number} colIndex The column index
40188      * @return {Boolean}
40189      */
40190     isHidden : function(colIndex){
40191         return this.config[colIndex].hidden;
40192     },
40193
40194
40195     /**
40196      * Returns true if the column width cannot be changed
40197      */
40198     isFixed : function(colIndex){
40199         return this.config[colIndex].fixed;
40200     },
40201
40202     /**
40203      * Returns true if the column can be resized
40204      * @return {Boolean}
40205      */
40206     isResizable : function(colIndex){
40207         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40208     },
40209     /**
40210      * Sets if a column is hidden.
40211      * @param {Number} colIndex The column index
40212      * @param {Boolean} hidden True if the column is hidden
40213      */
40214     setHidden : function(colIndex, hidden){
40215         this.config[colIndex].hidden = hidden;
40216         this.totalWidth = null;
40217         this.fireEvent("hiddenchange", this, colIndex, hidden);
40218     },
40219
40220     /**
40221      * Sets the editor for a column.
40222      * @param {Number} col The column index
40223      * @param {Object} editor The editor object
40224      */
40225     setEditor : function(col, editor){
40226         this.config[col].editor = editor;
40227     },
40228     /**
40229      * Add a column (experimental...) - defaults to adding to the end..
40230      * @param {Object} config 
40231     */
40232     addColumn : function(c)
40233     {
40234     
40235         var i = this.config.length;
40236         this.config[i] = c;
40237         
40238         if(typeof c.dataIndex == "undefined"){
40239             c.dataIndex = i;
40240         }
40241         if(typeof c.renderer == "string"){
40242             c.renderer = Roo.util.Format[c.renderer];
40243         }
40244         if(typeof c.id == "undefined"){
40245             c.id = Roo.id();
40246         }
40247         if(c.editor && c.editor.xtype){
40248             c.editor  = Roo.factory(c.editor, Roo.grid);
40249         }
40250         if(c.editor && c.editor.isFormField){
40251             c.editor = new Roo.grid.GridEditor(c.editor);
40252         }
40253         this.lookup[c.id] = c;
40254     }
40255     
40256 });
40257
40258 Roo.grid.ColumnModel.defaultRenderer = function(value)
40259 {
40260     if(typeof value == "object") {
40261         return value;
40262     }
40263         if(typeof value == "string" && value.length < 1){
40264             return "&#160;";
40265         }
40266     
40267         return String.format("{0}", value);
40268 };
40269
40270 // Alias for backwards compatibility
40271 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40272 /*
40273  * Based on:
40274  * Ext JS Library 1.1.1
40275  * Copyright(c) 2006-2007, Ext JS, LLC.
40276  *
40277  * Originally Released Under LGPL - original licence link has changed is not relivant.
40278  *
40279  * Fork - LGPL
40280  * <script type="text/javascript">
40281  */
40282
40283 /**
40284  * @class Roo.grid.AbstractSelectionModel
40285  * @extends Roo.util.Observable
40286  * @abstract
40287  * Abstract base class for grid SelectionModels.  It provides the interface that should be
40288  * implemented by descendant classes.  This class should not be directly instantiated.
40289  * @constructor
40290  */
40291 Roo.grid.AbstractSelectionModel = function(){
40292     this.locked = false;
40293     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40294 };
40295
40296 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
40297     /** @ignore Called by the grid automatically. Do not call directly. */
40298     init : function(grid){
40299         this.grid = grid;
40300         this.initEvents();
40301     },
40302
40303     /**
40304      * Locks the selections.
40305      */
40306     lock : function(){
40307         this.locked = true;
40308     },
40309
40310     /**
40311      * Unlocks the selections.
40312      */
40313     unlock : function(){
40314         this.locked = false;
40315     },
40316
40317     /**
40318      * Returns true if the selections are locked.
40319      * @return {Boolean}
40320      */
40321     isLocked : function(){
40322         return this.locked;
40323     }
40324 });/*
40325  * Based on:
40326  * Ext JS Library 1.1.1
40327  * Copyright(c) 2006-2007, Ext JS, LLC.
40328  *
40329  * Originally Released Under LGPL - original licence link has changed is not relivant.
40330  *
40331  * Fork - LGPL
40332  * <script type="text/javascript">
40333  */
40334 /**
40335  * @extends Roo.grid.AbstractSelectionModel
40336  * @class Roo.grid.RowSelectionModel
40337  * The default SelectionModel used by {@link Roo.grid.Grid}.
40338  * It supports multiple selections and keyboard selection/navigation. 
40339  * @constructor
40340  * @param {Object} config
40341  */
40342 Roo.grid.RowSelectionModel = function(config){
40343     Roo.apply(this, config);
40344     this.selections = new Roo.util.MixedCollection(false, function(o){
40345         return o.id;
40346     });
40347
40348     this.last = false;
40349     this.lastActive = false;
40350
40351     this.addEvents({
40352         /**
40353         * @event selectionchange
40354         * Fires when the selection changes
40355         * @param {SelectionModel} this
40356         */
40357        "selectionchange" : true,
40358        /**
40359         * @event afterselectionchange
40360         * Fires after the selection changes (eg. by key press or clicking)
40361         * @param {SelectionModel} this
40362         */
40363        "afterselectionchange" : true,
40364        /**
40365         * @event beforerowselect
40366         * Fires when a row is selected being selected, return false to cancel.
40367         * @param {SelectionModel} this
40368         * @param {Number} rowIndex The selected index
40369         * @param {Boolean} keepExisting False if other selections will be cleared
40370         */
40371        "beforerowselect" : true,
40372        /**
40373         * @event rowselect
40374         * Fires when a row is selected.
40375         * @param {SelectionModel} this
40376         * @param {Number} rowIndex The selected index
40377         * @param {Roo.data.Record} r The record
40378         */
40379        "rowselect" : true,
40380        /**
40381         * @event rowdeselect
40382         * Fires when a row is deselected.
40383         * @param {SelectionModel} this
40384         * @param {Number} rowIndex The selected index
40385         */
40386         "rowdeselect" : true
40387     });
40388     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40389     this.locked = false;
40390 };
40391
40392 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
40393     /**
40394      * @cfg {Boolean} singleSelect
40395      * True to allow selection of only one row at a time (defaults to false)
40396      */
40397     singleSelect : false,
40398
40399     // private
40400     initEvents : function(){
40401
40402         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40403             this.grid.on("mousedown", this.handleMouseDown, this);
40404         }else{ // allow click to work like normal
40405             this.grid.on("rowclick", this.handleDragableRowClick, this);
40406         }
40407         // bootstrap does not have a view..
40408         var view = this.grid.view ? this.grid.view : this.grid;
40409         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40410             "up" : function(e){
40411                 if(!e.shiftKey){
40412                     this.selectPrevious(e.shiftKey);
40413                 }else if(this.last !== false && this.lastActive !== false){
40414                     var last = this.last;
40415                     this.selectRange(this.last,  this.lastActive-1);
40416                     view.focusRow(this.lastActive);
40417                     if(last !== false){
40418                         this.last = last;
40419                     }
40420                 }else{
40421                     this.selectFirstRow();
40422                 }
40423                 this.fireEvent("afterselectionchange", this);
40424             },
40425             "down" : function(e){
40426                 if(!e.shiftKey){
40427                     this.selectNext(e.shiftKey);
40428                 }else if(this.last !== false && this.lastActive !== false){
40429                     var last = this.last;
40430                     this.selectRange(this.last,  this.lastActive+1);
40431                     view.focusRow(this.lastActive);
40432                     if(last !== false){
40433                         this.last = last;
40434                     }
40435                 }else{
40436                     this.selectFirstRow();
40437                 }
40438                 this.fireEvent("afterselectionchange", this);
40439             },
40440             scope: this
40441         });
40442
40443          
40444         view.on("refresh", this.onRefresh, this);
40445         view.on("rowupdated", this.onRowUpdated, this);
40446         view.on("rowremoved", this.onRemove, this);
40447     },
40448
40449     // private
40450     onRefresh : function(){
40451         var ds = this.grid.ds, i, v = this.grid.view;
40452         var s = this.selections;
40453         s.each(function(r){
40454             if((i = ds.indexOfId(r.id)) != -1){
40455                 v.onRowSelect(i);
40456                 s.add(ds.getAt(i)); // updating the selection relate data
40457             }else{
40458                 s.remove(r);
40459             }
40460         });
40461     },
40462
40463     // private
40464     onRemove : function(v, index, r){
40465         this.selections.remove(r);
40466     },
40467
40468     // private
40469     onRowUpdated : function(v, index, r){
40470         if(this.isSelected(r)){
40471             v.onRowSelect(index);
40472         }
40473     },
40474
40475     /**
40476      * Select records.
40477      * @param {Array} records The records to select
40478      * @param {Boolean} keepExisting (optional) True to keep existing selections
40479      */
40480     selectRecords : function(records, keepExisting){
40481         if(!keepExisting){
40482             this.clearSelections();
40483         }
40484         var ds = this.grid.ds;
40485         for(var i = 0, len = records.length; i < len; i++){
40486             this.selectRow(ds.indexOf(records[i]), true);
40487         }
40488     },
40489
40490     /**
40491      * Gets the number of selected rows.
40492      * @return {Number}
40493      */
40494     getCount : function(){
40495         return this.selections.length;
40496     },
40497
40498     /**
40499      * Selects the first row in the grid.
40500      */
40501     selectFirstRow : function(){
40502         this.selectRow(0);
40503     },
40504
40505     /**
40506      * Select the last row.
40507      * @param {Boolean} keepExisting (optional) True to keep existing selections
40508      */
40509     selectLastRow : function(keepExisting){
40510         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40511     },
40512
40513     /**
40514      * Selects the row immediately following the last selected row.
40515      * @param {Boolean} keepExisting (optional) True to keep existing selections
40516      */
40517     selectNext : function(keepExisting){
40518         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40519             this.selectRow(this.last+1, keepExisting);
40520             var view = this.grid.view ? this.grid.view : this.grid;
40521             view.focusRow(this.last);
40522         }
40523     },
40524
40525     /**
40526      * Selects the row that precedes the last selected row.
40527      * @param {Boolean} keepExisting (optional) True to keep existing selections
40528      */
40529     selectPrevious : function(keepExisting){
40530         if(this.last){
40531             this.selectRow(this.last-1, keepExisting);
40532             var view = this.grid.view ? this.grid.view : this.grid;
40533             view.focusRow(this.last);
40534         }
40535     },
40536
40537     /**
40538      * Returns the selected records
40539      * @return {Array} Array of selected records
40540      */
40541     getSelections : function(){
40542         return [].concat(this.selections.items);
40543     },
40544
40545     /**
40546      * Returns the first selected record.
40547      * @return {Record}
40548      */
40549     getSelected : function(){
40550         return this.selections.itemAt(0);
40551     },
40552
40553
40554     /**
40555      * Clears all selections.
40556      */
40557     clearSelections : function(fast){
40558         if(this.locked) {
40559             return;
40560         }
40561         if(fast !== true){
40562             var ds = this.grid.ds;
40563             var s = this.selections;
40564             s.each(function(r){
40565                 this.deselectRow(ds.indexOfId(r.id));
40566             }, this);
40567             s.clear();
40568         }else{
40569             this.selections.clear();
40570         }
40571         this.last = false;
40572     },
40573
40574
40575     /**
40576      * Selects all rows.
40577      */
40578     selectAll : function(){
40579         if(this.locked) {
40580             return;
40581         }
40582         this.selections.clear();
40583         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40584             this.selectRow(i, true);
40585         }
40586     },
40587
40588     /**
40589      * Returns True if there is a selection.
40590      * @return {Boolean}
40591      */
40592     hasSelection : function(){
40593         return this.selections.length > 0;
40594     },
40595
40596     /**
40597      * Returns True if the specified row is selected.
40598      * @param {Number/Record} record The record or index of the record to check
40599      * @return {Boolean}
40600      */
40601     isSelected : function(index){
40602         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40603         return (r && this.selections.key(r.id) ? true : false);
40604     },
40605
40606     /**
40607      * Returns True if the specified record id is selected.
40608      * @param {String} id The id of record to check
40609      * @return {Boolean}
40610      */
40611     isIdSelected : function(id){
40612         return (this.selections.key(id) ? true : false);
40613     },
40614
40615     // private
40616     handleMouseDown : function(e, t)
40617     {
40618         var view = this.grid.view ? this.grid.view : this.grid;
40619         var rowIndex;
40620         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40621             return;
40622         };
40623         if(e.shiftKey && this.last !== false){
40624             var last = this.last;
40625             this.selectRange(last, rowIndex, e.ctrlKey);
40626             this.last = last; // reset the last
40627             view.focusRow(rowIndex);
40628         }else{
40629             var isSelected = this.isSelected(rowIndex);
40630             if(e.button !== 0 && isSelected){
40631                 view.focusRow(rowIndex);
40632             }else if(e.ctrlKey && isSelected){
40633                 this.deselectRow(rowIndex);
40634             }else if(!isSelected){
40635                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40636                 view.focusRow(rowIndex);
40637             }
40638         }
40639         this.fireEvent("afterselectionchange", this);
40640     },
40641     // private
40642     handleDragableRowClick :  function(grid, rowIndex, e) 
40643     {
40644         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40645             this.selectRow(rowIndex, false);
40646             var view = this.grid.view ? this.grid.view : this.grid;
40647             view.focusRow(rowIndex);
40648              this.fireEvent("afterselectionchange", this);
40649         }
40650     },
40651     
40652     /**
40653      * Selects multiple rows.
40654      * @param {Array} rows Array of the indexes of the row to select
40655      * @param {Boolean} keepExisting (optional) True to keep existing selections
40656      */
40657     selectRows : function(rows, keepExisting){
40658         if(!keepExisting){
40659             this.clearSelections();
40660         }
40661         for(var i = 0, len = rows.length; i < len; i++){
40662             this.selectRow(rows[i], true);
40663         }
40664     },
40665
40666     /**
40667      * Selects a range of rows. All rows in between startRow and endRow are also selected.
40668      * @param {Number} startRow The index of the first row in the range
40669      * @param {Number} endRow The index of the last row in the range
40670      * @param {Boolean} keepExisting (optional) True to retain existing selections
40671      */
40672     selectRange : function(startRow, endRow, keepExisting){
40673         if(this.locked) {
40674             return;
40675         }
40676         if(!keepExisting){
40677             this.clearSelections();
40678         }
40679         if(startRow <= endRow){
40680             for(var i = startRow; i <= endRow; i++){
40681                 this.selectRow(i, true);
40682             }
40683         }else{
40684             for(var i = startRow; i >= endRow; i--){
40685                 this.selectRow(i, true);
40686             }
40687         }
40688     },
40689
40690     /**
40691      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40692      * @param {Number} startRow The index of the first row in the range
40693      * @param {Number} endRow The index of the last row in the range
40694      */
40695     deselectRange : function(startRow, endRow, preventViewNotify){
40696         if(this.locked) {
40697             return;
40698         }
40699         for(var i = startRow; i <= endRow; i++){
40700             this.deselectRow(i, preventViewNotify);
40701         }
40702     },
40703
40704     /**
40705      * Selects a row.
40706      * @param {Number} row The index of the row to select
40707      * @param {Boolean} keepExisting (optional) True to keep existing selections
40708      */
40709     selectRow : function(index, keepExisting, preventViewNotify){
40710         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40711             return;
40712         }
40713         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40714             if(!keepExisting || this.singleSelect){
40715                 this.clearSelections();
40716             }
40717             var r = this.grid.ds.getAt(index);
40718             this.selections.add(r);
40719             this.last = this.lastActive = index;
40720             if(!preventViewNotify){
40721                 var view = this.grid.view ? this.grid.view : this.grid;
40722                 view.onRowSelect(index);
40723             }
40724             this.fireEvent("rowselect", this, index, r);
40725             this.fireEvent("selectionchange", this);
40726         }
40727     },
40728
40729     /**
40730      * Deselects a row.
40731      * @param {Number} row The index of the row to deselect
40732      */
40733     deselectRow : function(index, preventViewNotify){
40734         if(this.locked) {
40735             return;
40736         }
40737         if(this.last == index){
40738             this.last = false;
40739         }
40740         if(this.lastActive == index){
40741             this.lastActive = false;
40742         }
40743         var r = this.grid.ds.getAt(index);
40744         this.selections.remove(r);
40745         if(!preventViewNotify){
40746             var view = this.grid.view ? this.grid.view : this.grid;
40747             view.onRowDeselect(index);
40748         }
40749         this.fireEvent("rowdeselect", this, index);
40750         this.fireEvent("selectionchange", this);
40751     },
40752
40753     // private
40754     restoreLast : function(){
40755         if(this._last){
40756             this.last = this._last;
40757         }
40758     },
40759
40760     // private
40761     acceptsNav : function(row, col, cm){
40762         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40763     },
40764
40765     // private
40766     onEditorKey : function(field, e){
40767         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
40768         if(k == e.TAB){
40769             e.stopEvent();
40770             ed.completeEdit();
40771             if(e.shiftKey){
40772                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40773             }else{
40774                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40775             }
40776         }else if(k == e.ENTER && !e.ctrlKey){
40777             e.stopEvent();
40778             ed.completeEdit();
40779             if(e.shiftKey){
40780                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40781             }else{
40782                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40783             }
40784         }else if(k == e.ESC){
40785             ed.cancelEdit();
40786         }
40787         if(newCell){
40788             g.startEditing(newCell[0], newCell[1]);
40789         }
40790     }
40791 });/*
40792  * Based on:
40793  * Ext JS Library 1.1.1
40794  * Copyright(c) 2006-2007, Ext JS, LLC.
40795  *
40796  * Originally Released Under LGPL - original licence link has changed is not relivant.
40797  *
40798  * Fork - LGPL
40799  * <script type="text/javascript">
40800  */
40801 /**
40802  * @class Roo.grid.CellSelectionModel
40803  * @extends Roo.grid.AbstractSelectionModel
40804  * This class provides the basic implementation for cell selection in a grid.
40805  * @constructor
40806  * @param {Object} config The object containing the configuration of this model.
40807  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40808  */
40809 Roo.grid.CellSelectionModel = function(config){
40810     Roo.apply(this, config);
40811
40812     this.selection = null;
40813
40814     this.addEvents({
40815         /**
40816              * @event beforerowselect
40817              * Fires before a cell is selected.
40818              * @param {SelectionModel} this
40819              * @param {Number} rowIndex The selected row index
40820              * @param {Number} colIndex The selected cell index
40821              */
40822             "beforecellselect" : true,
40823         /**
40824              * @event cellselect
40825              * Fires when a cell is selected.
40826              * @param {SelectionModel} this
40827              * @param {Number} rowIndex The selected row index
40828              * @param {Number} colIndex The selected cell index
40829              */
40830             "cellselect" : true,
40831         /**
40832              * @event selectionchange
40833              * Fires when the active selection changes.
40834              * @param {SelectionModel} this
40835              * @param {Object} selection null for no selection or an object (o) with two properties
40836                 <ul>
40837                 <li>o.record: the record object for the row the selection is in</li>
40838                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40839                 </ul>
40840              */
40841             "selectionchange" : true,
40842         /**
40843              * @event tabend
40844              * Fires when the tab (or enter) was pressed on the last editable cell
40845              * You can use this to trigger add new row.
40846              * @param {SelectionModel} this
40847              */
40848             "tabend" : true,
40849          /**
40850              * @event beforeeditnext
40851              * Fires before the next editable sell is made active
40852              * You can use this to skip to another cell or fire the tabend
40853              *    if you set cell to false
40854              * @param {Object} eventdata object : { cell : [ row, col ] } 
40855              */
40856             "beforeeditnext" : true
40857     });
40858     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40859 };
40860
40861 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40862     
40863     enter_is_tab: false,
40864
40865     /** @ignore */
40866     initEvents : function(){
40867         this.grid.on("mousedown", this.handleMouseDown, this);
40868         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40869         var view = this.grid.view;
40870         view.on("refresh", this.onViewChange, this);
40871         view.on("rowupdated", this.onRowUpdated, this);
40872         view.on("beforerowremoved", this.clearSelections, this);
40873         view.on("beforerowsinserted", this.clearSelections, this);
40874         if(this.grid.isEditor){
40875             this.grid.on("beforeedit", this.beforeEdit,  this);
40876         }
40877     },
40878
40879         //private
40880     beforeEdit : function(e){
40881         this.select(e.row, e.column, false, true, e.record);
40882     },
40883
40884         //private
40885     onRowUpdated : function(v, index, r){
40886         if(this.selection && this.selection.record == r){
40887             v.onCellSelect(index, this.selection.cell[1]);
40888         }
40889     },
40890
40891         //private
40892     onViewChange : function(){
40893         this.clearSelections(true);
40894     },
40895
40896         /**
40897          * Returns the currently selected cell,.
40898          * @return {Array} The selected cell (row, column) or null if none selected.
40899          */
40900     getSelectedCell : function(){
40901         return this.selection ? this.selection.cell : null;
40902     },
40903
40904     /**
40905      * Clears all selections.
40906      * @param {Boolean} true to prevent the gridview from being notified about the change.
40907      */
40908     clearSelections : function(preventNotify){
40909         var s = this.selection;
40910         if(s){
40911             if(preventNotify !== true){
40912                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40913             }
40914             this.selection = null;
40915             this.fireEvent("selectionchange", this, null);
40916         }
40917     },
40918
40919     /**
40920      * Returns true if there is a selection.
40921      * @return {Boolean}
40922      */
40923     hasSelection : function(){
40924         return this.selection ? true : false;
40925     },
40926
40927     /** @ignore */
40928     handleMouseDown : function(e, t){
40929         var v = this.grid.getView();
40930         if(this.isLocked()){
40931             return;
40932         };
40933         var row = v.findRowIndex(t);
40934         var cell = v.findCellIndex(t);
40935         if(row !== false && cell !== false){
40936             this.select(row, cell);
40937         }
40938     },
40939
40940     /**
40941      * Selects a cell.
40942      * @param {Number} rowIndex
40943      * @param {Number} collIndex
40944      */
40945     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40946         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40947             this.clearSelections();
40948             r = r || this.grid.dataSource.getAt(rowIndex);
40949             this.selection = {
40950                 record : r,
40951                 cell : [rowIndex, colIndex]
40952             };
40953             if(!preventViewNotify){
40954                 var v = this.grid.getView();
40955                 v.onCellSelect(rowIndex, colIndex);
40956                 if(preventFocus !== true){
40957                     v.focusCell(rowIndex, colIndex);
40958                 }
40959             }
40960             this.fireEvent("cellselect", this, rowIndex, colIndex);
40961             this.fireEvent("selectionchange", this, this.selection);
40962         }
40963     },
40964
40965         //private
40966     isSelectable : function(rowIndex, colIndex, cm){
40967         return !cm.isHidden(colIndex);
40968     },
40969
40970     /** @ignore */
40971     handleKeyDown : function(e){
40972         //Roo.log('Cell Sel Model handleKeyDown');
40973         if(!e.isNavKeyPress()){
40974             return;
40975         }
40976         var g = this.grid, s = this.selection;
40977         if(!s){
40978             e.stopEvent();
40979             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40980             if(cell){
40981                 this.select(cell[0], cell[1]);
40982             }
40983             return;
40984         }
40985         var sm = this;
40986         var walk = function(row, col, step){
40987             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40988         };
40989         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40990         var newCell;
40991
40992       
40993
40994         switch(k){
40995             case e.TAB:
40996                 // handled by onEditorKey
40997                 if (g.isEditor && g.editing) {
40998                     return;
40999                 }
41000                 if(e.shiftKey) {
41001                     newCell = walk(r, c-1, -1);
41002                 } else {
41003                     newCell = walk(r, c+1, 1);
41004                 }
41005                 break;
41006             
41007             case e.DOWN:
41008                newCell = walk(r+1, c, 1);
41009                 break;
41010             
41011             case e.UP:
41012                 newCell = walk(r-1, c, -1);
41013                 break;
41014             
41015             case e.RIGHT:
41016                 newCell = walk(r, c+1, 1);
41017                 break;
41018             
41019             case e.LEFT:
41020                 newCell = walk(r, c-1, -1);
41021                 break;
41022             
41023             case e.ENTER:
41024                 
41025                 if(g.isEditor && !g.editing){
41026                    g.startEditing(r, c);
41027                    e.stopEvent();
41028                    return;
41029                 }
41030                 
41031                 
41032              break;
41033         };
41034         if(newCell){
41035             this.select(newCell[0], newCell[1]);
41036             e.stopEvent();
41037             
41038         }
41039     },
41040
41041     acceptsNav : function(row, col, cm){
41042         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41043     },
41044     /**
41045      * Selects a cell.
41046      * @param {Number} field (not used) - as it's normally used as a listener
41047      * @param {Number} e - event - fake it by using
41048      *
41049      * var e = Roo.EventObjectImpl.prototype;
41050      * e.keyCode = e.TAB
41051      *
41052      * 
41053      */
41054     onEditorKey : function(field, e){
41055         
41056         var k = e.getKey(),
41057             newCell,
41058             g = this.grid,
41059             ed = g.activeEditor,
41060             forward = false;
41061         ///Roo.log('onEditorKey' + k);
41062         
41063         
41064         if (this.enter_is_tab && k == e.ENTER) {
41065             k = e.TAB;
41066         }
41067         
41068         if(k == e.TAB){
41069             if(e.shiftKey){
41070                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41071             }else{
41072                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41073                 forward = true;
41074             }
41075             
41076             e.stopEvent();
41077             
41078         } else if(k == e.ENTER &&  !e.ctrlKey){
41079             ed.completeEdit();
41080             e.stopEvent();
41081             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41082         
41083                 } else if(k == e.ESC){
41084             ed.cancelEdit();
41085         }
41086                 
41087         if (newCell) {
41088             var ecall = { cell : newCell, forward : forward };
41089             this.fireEvent('beforeeditnext', ecall );
41090             newCell = ecall.cell;
41091                         forward = ecall.forward;
41092         }
41093                 
41094         if(newCell){
41095             //Roo.log('next cell after edit');
41096             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41097         } else if (forward) {
41098             // tabbed past last
41099             this.fireEvent.defer(100, this, ['tabend',this]);
41100         }
41101     }
41102 });/*
41103  * Based on:
41104  * Ext JS Library 1.1.1
41105  * Copyright(c) 2006-2007, Ext JS, LLC.
41106  *
41107  * Originally Released Under LGPL - original licence link has changed is not relivant.
41108  *
41109  * Fork - LGPL
41110  * <script type="text/javascript">
41111  */
41112  
41113 /**
41114  * @class Roo.grid.EditorGrid
41115  * @extends Roo.grid.Grid
41116  * Class for creating and editable grid.
41117  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
41118  * The container MUST have some type of size defined for the grid to fill. The container will be 
41119  * automatically set to position relative if it isn't already.
41120  * @param {Object} dataSource The data model to bind to
41121  * @param {Object} colModel The column model with info about this grid's columns
41122  */
41123 Roo.grid.EditorGrid = function(container, config){
41124     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41125     this.getGridEl().addClass("xedit-grid");
41126
41127     if(!this.selModel){
41128         this.selModel = new Roo.grid.CellSelectionModel();
41129     }
41130
41131     this.activeEditor = null;
41132
41133         this.addEvents({
41134             /**
41135              * @event beforeedit
41136              * Fires before cell editing is triggered. The edit event object has the following properties <br />
41137              * <ul style="padding:5px;padding-left:16px;">
41138              * <li>grid - This grid</li>
41139              * <li>record - The record being edited</li>
41140              * <li>field - The field name being edited</li>
41141              * <li>value - The value for the field being edited.</li>
41142              * <li>row - The grid row index</li>
41143              * <li>column - The grid column index</li>
41144              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41145              * </ul>
41146              * @param {Object} e An edit event (see above for description)
41147              */
41148             "beforeedit" : true,
41149             /**
41150              * @event afteredit
41151              * Fires after a cell is edited. <br />
41152              * <ul style="padding:5px;padding-left:16px;">
41153              * <li>grid - This grid</li>
41154              * <li>record - The record being edited</li>
41155              * <li>field - The field name being edited</li>
41156              * <li>value - The value being set</li>
41157              * <li>originalValue - The original value for the field, before the edit.</li>
41158              * <li>row - The grid row index</li>
41159              * <li>column - The grid column index</li>
41160              * </ul>
41161              * @param {Object} e An edit event (see above for description)
41162              */
41163             "afteredit" : true,
41164             /**
41165              * @event validateedit
41166              * Fires after a cell is edited, but before the value is set in the record. 
41167          * You can use this to modify the value being set in the field, Return false
41168              * to cancel the change. The edit event object has the following properties <br />
41169              * <ul style="padding:5px;padding-left:16px;">
41170          * <li>editor - This editor</li>
41171              * <li>grid - This grid</li>
41172              * <li>record - The record being edited</li>
41173              * <li>field - The field name being edited</li>
41174              * <li>value - The value being set</li>
41175              * <li>originalValue - The original value for the field, before the edit.</li>
41176              * <li>row - The grid row index</li>
41177              * <li>column - The grid column index</li>
41178              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41179              * </ul>
41180              * @param {Object} e An edit event (see above for description)
41181              */
41182             "validateedit" : true
41183         });
41184     this.on("bodyscroll", this.stopEditing,  this);
41185     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
41186 };
41187
41188 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41189     /**
41190      * @cfg {Number} clicksToEdit
41191      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41192      */
41193     clicksToEdit: 2,
41194
41195     // private
41196     isEditor : true,
41197     // private
41198     trackMouseOver: false, // causes very odd FF errors
41199
41200     onCellDblClick : function(g, row, col){
41201         this.startEditing(row, col);
41202     },
41203
41204     onEditComplete : function(ed, value, startValue){
41205         this.editing = false;
41206         this.activeEditor = null;
41207         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41208         var r = ed.record;
41209         var field = this.colModel.getDataIndex(ed.col);
41210         var e = {
41211             grid: this,
41212             record: r,
41213             field: field,
41214             originalValue: startValue,
41215             value: value,
41216             row: ed.row,
41217             column: ed.col,
41218             cancel:false,
41219             editor: ed
41220         };
41221         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41222         cell.show();
41223           
41224         if(String(value) !== String(startValue)){
41225             
41226             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41227                 r.set(field, e.value);
41228                 // if we are dealing with a combo box..
41229                 // then we also set the 'name' colum to be the displayField
41230                 if (ed.field.displayField && ed.field.name) {
41231                     r.set(ed.field.name, ed.field.el.dom.value);
41232                 }
41233                 
41234                 delete e.cancel; //?? why!!!
41235                 this.fireEvent("afteredit", e);
41236             }
41237         } else {
41238             this.fireEvent("afteredit", e); // always fire it!
41239         }
41240         this.view.focusCell(ed.row, ed.col);
41241     },
41242
41243     /**
41244      * Starts editing the specified for the specified row/column
41245      * @param {Number} rowIndex
41246      * @param {Number} colIndex
41247      */
41248     startEditing : function(row, col){
41249         this.stopEditing();
41250         if(this.colModel.isCellEditable(col, row)){
41251             this.view.ensureVisible(row, col, true);
41252           
41253             var r = this.dataSource.getAt(row);
41254             var field = this.colModel.getDataIndex(col);
41255             var cell = Roo.get(this.view.getCell(row,col));
41256             var e = {
41257                 grid: this,
41258                 record: r,
41259                 field: field,
41260                 value: r.data[field],
41261                 row: row,
41262                 column: col,
41263                 cancel:false 
41264             };
41265             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41266                 this.editing = true;
41267                 var ed = this.colModel.getCellEditor(col, row);
41268                 
41269                 if (!ed) {
41270                     return;
41271                 }
41272                 if(!ed.rendered){
41273                     ed.render(ed.parentEl || document.body);
41274                 }
41275                 ed.field.reset();
41276                
41277                 cell.hide();
41278                 
41279                 (function(){ // complex but required for focus issues in safari, ie and opera
41280                     ed.row = row;
41281                     ed.col = col;
41282                     ed.record = r;
41283                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
41284                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
41285                     this.activeEditor = ed;
41286                     var v = r.data[field];
41287                     ed.startEdit(this.view.getCell(row, col), v);
41288                     // combo's with 'displayField and name set
41289                     if (ed.field.displayField && ed.field.name) {
41290                         ed.field.el.dom.value = r.data[ed.field.name];
41291                     }
41292                     
41293                     
41294                 }).defer(50, this);
41295             }
41296         }
41297     },
41298         
41299     /**
41300      * Stops any active editing
41301      */
41302     stopEditing : function(){
41303         if(this.activeEditor){
41304             this.activeEditor.completeEdit();
41305         }
41306         this.activeEditor = null;
41307     },
41308         
41309          /**
41310      * Called to get grid's drag proxy text, by default returns this.ddText.
41311      * @return {String}
41312      */
41313     getDragDropText : function(){
41314         var count = this.selModel.getSelectedCell() ? 1 : 0;
41315         return String.format(this.ddText, count, count == 1 ? '' : 's');
41316     }
41317         
41318 });/*
41319  * Based on:
41320  * Ext JS Library 1.1.1
41321  * Copyright(c) 2006-2007, Ext JS, LLC.
41322  *
41323  * Originally Released Under LGPL - original licence link has changed is not relivant.
41324  *
41325  * Fork - LGPL
41326  * <script type="text/javascript">
41327  */
41328
41329 // private - not really -- you end up using it !
41330 // This is a support class used internally by the Grid components
41331
41332 /**
41333  * @class Roo.grid.GridEditor
41334  * @extends Roo.Editor
41335  * Class for creating and editable grid elements.
41336  * @param {Object} config any settings (must include field)
41337  */
41338 Roo.grid.GridEditor = function(field, config){
41339     if (!config && field.field) {
41340         config = field;
41341         field = Roo.factory(config.field, Roo.form);
41342     }
41343     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41344     field.monitorTab = false;
41345 };
41346
41347 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41348     
41349     /**
41350      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41351      */
41352     
41353     alignment: "tl-tl",
41354     autoSize: "width",
41355     hideEl : false,
41356     cls: "x-small-editor x-grid-editor",
41357     shim:false,
41358     shadow:"frame"
41359 });/*
41360  * Based on:
41361  * Ext JS Library 1.1.1
41362  * Copyright(c) 2006-2007, Ext JS, LLC.
41363  *
41364  * Originally Released Under LGPL - original licence link has changed is not relivant.
41365  *
41366  * Fork - LGPL
41367  * <script type="text/javascript">
41368  */
41369   
41370
41371   
41372 Roo.grid.PropertyRecord = Roo.data.Record.create([
41373     {name:'name',type:'string'},  'value'
41374 ]);
41375
41376
41377 Roo.grid.PropertyStore = function(grid, source){
41378     this.grid = grid;
41379     this.store = new Roo.data.Store({
41380         recordType : Roo.grid.PropertyRecord
41381     });
41382     this.store.on('update', this.onUpdate,  this);
41383     if(source){
41384         this.setSource(source);
41385     }
41386     Roo.grid.PropertyStore.superclass.constructor.call(this);
41387 };
41388
41389
41390
41391 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41392     setSource : function(o){
41393         this.source = o;
41394         this.store.removeAll();
41395         var data = [];
41396         for(var k in o){
41397             if(this.isEditableValue(o[k])){
41398                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41399             }
41400         }
41401         this.store.loadRecords({records: data}, {}, true);
41402     },
41403
41404     onUpdate : function(ds, record, type){
41405         if(type == Roo.data.Record.EDIT){
41406             var v = record.data['value'];
41407             var oldValue = record.modified['value'];
41408             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41409                 this.source[record.id] = v;
41410                 record.commit();
41411                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41412             }else{
41413                 record.reject();
41414             }
41415         }
41416     },
41417
41418     getProperty : function(row){
41419        return this.store.getAt(row);
41420     },
41421
41422     isEditableValue: function(val){
41423         if(val && val instanceof Date){
41424             return true;
41425         }else if(typeof val == 'object' || typeof val == 'function'){
41426             return false;
41427         }
41428         return true;
41429     },
41430
41431     setValue : function(prop, value){
41432         this.source[prop] = value;
41433         this.store.getById(prop).set('value', value);
41434     },
41435
41436     getSource : function(){
41437         return this.source;
41438     }
41439 });
41440
41441 Roo.grid.PropertyColumnModel = function(grid, store){
41442     this.grid = grid;
41443     var g = Roo.grid;
41444     g.PropertyColumnModel.superclass.constructor.call(this, [
41445         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41446         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41447     ]);
41448     this.store = store;
41449     this.bselect = Roo.DomHelper.append(document.body, {
41450         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41451             {tag: 'option', value: 'true', html: 'true'},
41452             {tag: 'option', value: 'false', html: 'false'}
41453         ]
41454     });
41455     Roo.id(this.bselect);
41456     var f = Roo.form;
41457     this.editors = {
41458         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41459         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41460         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41461         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41462         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41463     };
41464     this.renderCellDelegate = this.renderCell.createDelegate(this);
41465     this.renderPropDelegate = this.renderProp.createDelegate(this);
41466 };
41467
41468 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41469     
41470     
41471     nameText : 'Name',
41472     valueText : 'Value',
41473     
41474     dateFormat : 'm/j/Y',
41475     
41476     
41477     renderDate : function(dateVal){
41478         return dateVal.dateFormat(this.dateFormat);
41479     },
41480
41481     renderBool : function(bVal){
41482         return bVal ? 'true' : 'false';
41483     },
41484
41485     isCellEditable : function(colIndex, rowIndex){
41486         return colIndex == 1;
41487     },
41488
41489     getRenderer : function(col){
41490         return col == 1 ?
41491             this.renderCellDelegate : this.renderPropDelegate;
41492     },
41493
41494     renderProp : function(v){
41495         return this.getPropertyName(v);
41496     },
41497
41498     renderCell : function(val){
41499         var rv = val;
41500         if(val instanceof Date){
41501             rv = this.renderDate(val);
41502         }else if(typeof val == 'boolean'){
41503             rv = this.renderBool(val);
41504         }
41505         return Roo.util.Format.htmlEncode(rv);
41506     },
41507
41508     getPropertyName : function(name){
41509         var pn = this.grid.propertyNames;
41510         return pn && pn[name] ? pn[name] : name;
41511     },
41512
41513     getCellEditor : function(colIndex, rowIndex){
41514         var p = this.store.getProperty(rowIndex);
41515         var n = p.data['name'], val = p.data['value'];
41516         
41517         if(typeof(this.grid.customEditors[n]) == 'string'){
41518             return this.editors[this.grid.customEditors[n]];
41519         }
41520         if(typeof(this.grid.customEditors[n]) != 'undefined'){
41521             return this.grid.customEditors[n];
41522         }
41523         if(val instanceof Date){
41524             return this.editors['date'];
41525         }else if(typeof val == 'number'){
41526             return this.editors['number'];
41527         }else if(typeof val == 'boolean'){
41528             return this.editors['boolean'];
41529         }else{
41530             return this.editors['string'];
41531         }
41532     }
41533 });
41534
41535 /**
41536  * @class Roo.grid.PropertyGrid
41537  * @extends Roo.grid.EditorGrid
41538  * This class represents the  interface of a component based property grid control.
41539  * <br><br>Usage:<pre><code>
41540  var grid = new Roo.grid.PropertyGrid("my-container-id", {
41541       
41542  });
41543  // set any options
41544  grid.render();
41545  * </code></pre>
41546   
41547  * @constructor
41548  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41549  * The container MUST have some type of size defined for the grid to fill. The container will be
41550  * automatically set to position relative if it isn't already.
41551  * @param {Object} config A config object that sets properties on this grid.
41552  */
41553 Roo.grid.PropertyGrid = function(container, config){
41554     config = config || {};
41555     var store = new Roo.grid.PropertyStore(this);
41556     this.store = store;
41557     var cm = new Roo.grid.PropertyColumnModel(this, store);
41558     store.store.sort('name', 'ASC');
41559     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41560         ds: store.store,
41561         cm: cm,
41562         enableColLock:false,
41563         enableColumnMove:false,
41564         stripeRows:false,
41565         trackMouseOver: false,
41566         clicksToEdit:1
41567     }, config));
41568     this.getGridEl().addClass('x-props-grid');
41569     this.lastEditRow = null;
41570     this.on('columnresize', this.onColumnResize, this);
41571     this.addEvents({
41572          /**
41573              * @event beforepropertychange
41574              * Fires before a property changes (return false to stop?)
41575              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41576              * @param {String} id Record Id
41577              * @param {String} newval New Value
41578          * @param {String} oldval Old Value
41579              */
41580         "beforepropertychange": true,
41581         /**
41582              * @event propertychange
41583              * Fires after a property changes
41584              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41585              * @param {String} id Record Id
41586              * @param {String} newval New Value
41587          * @param {String} oldval Old Value
41588              */
41589         "propertychange": true
41590     });
41591     this.customEditors = this.customEditors || {};
41592 };
41593 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41594     
41595      /**
41596      * @cfg {Object} customEditors map of colnames=> custom editors.
41597      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41598      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41599      * false disables editing of the field.
41600          */
41601     
41602       /**
41603      * @cfg {Object} propertyNames map of property Names to their displayed value
41604          */
41605     
41606     render : function(){
41607         Roo.grid.PropertyGrid.superclass.render.call(this);
41608         this.autoSize.defer(100, this);
41609     },
41610
41611     autoSize : function(){
41612         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41613         if(this.view){
41614             this.view.fitColumns();
41615         }
41616     },
41617
41618     onColumnResize : function(){
41619         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41620         this.autoSize();
41621     },
41622     /**
41623      * Sets the data for the Grid
41624      * accepts a Key => Value object of all the elements avaiable.
41625      * @param {Object} data  to appear in grid.
41626      */
41627     setSource : function(source){
41628         this.store.setSource(source);
41629         //this.autoSize();
41630     },
41631     /**
41632      * Gets all the data from the grid.
41633      * @return {Object} data  data stored in grid
41634      */
41635     getSource : function(){
41636         return this.store.getSource();
41637     }
41638 });/*
41639   
41640  * Licence LGPL
41641  
41642  */
41643  
41644 /**
41645  * @class Roo.grid.Calendar
41646  * @extends Roo.grid.Grid
41647  * This class extends the Grid to provide a calendar widget
41648  * <br><br>Usage:<pre><code>
41649  var grid = new Roo.grid.Calendar("my-container-id", {
41650      ds: myDataStore,
41651      cm: myColModel,
41652      selModel: mySelectionModel,
41653      autoSizeColumns: true,
41654      monitorWindowResize: false,
41655      trackMouseOver: true
41656      eventstore : real data store..
41657  });
41658  // set any options
41659  grid.render();
41660   
41661   * @constructor
41662  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41663  * The container MUST have some type of size defined for the grid to fill. The container will be
41664  * automatically set to position relative if it isn't already.
41665  * @param {Object} config A config object that sets properties on this grid.
41666  */
41667 Roo.grid.Calendar = function(container, config){
41668         // initialize the container
41669         this.container = Roo.get(container);
41670         this.container.update("");
41671         this.container.setStyle("overflow", "hidden");
41672     this.container.addClass('x-grid-container');
41673
41674     this.id = this.container.id;
41675
41676     Roo.apply(this, config);
41677     // check and correct shorthanded configs
41678     
41679     var rows = [];
41680     var d =1;
41681     for (var r = 0;r < 6;r++) {
41682         
41683         rows[r]=[];
41684         for (var c =0;c < 7;c++) {
41685             rows[r][c]= '';
41686         }
41687     }
41688     if (this.eventStore) {
41689         this.eventStore= Roo.factory(this.eventStore, Roo.data);
41690         this.eventStore.on('load',this.onLoad, this);
41691         this.eventStore.on('beforeload',this.clearEvents, this);
41692          
41693     }
41694     
41695     this.dataSource = new Roo.data.Store({
41696             proxy: new Roo.data.MemoryProxy(rows),
41697             reader: new Roo.data.ArrayReader({}, [
41698                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41699     });
41700
41701     this.dataSource.load();
41702     this.ds = this.dataSource;
41703     this.ds.xmodule = this.xmodule || false;
41704     
41705     
41706     var cellRender = function(v,x,r)
41707     {
41708         return String.format(
41709             '<div class="fc-day  fc-widget-content"><div>' +
41710                 '<div class="fc-event-container"></div>' +
41711                 '<div class="fc-day-number">{0}</div>'+
41712                 
41713                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41714             '</div></div>', v);
41715     
41716     }
41717     
41718     
41719     this.colModel = new Roo.grid.ColumnModel( [
41720         {
41721             xtype: 'ColumnModel',
41722             xns: Roo.grid,
41723             dataIndex : 'weekday0',
41724             header : 'Sunday',
41725             renderer : cellRender
41726         },
41727         {
41728             xtype: 'ColumnModel',
41729             xns: Roo.grid,
41730             dataIndex : 'weekday1',
41731             header : 'Monday',
41732             renderer : cellRender
41733         },
41734         {
41735             xtype: 'ColumnModel',
41736             xns: Roo.grid,
41737             dataIndex : 'weekday2',
41738             header : 'Tuesday',
41739             renderer : cellRender
41740         },
41741         {
41742             xtype: 'ColumnModel',
41743             xns: Roo.grid,
41744             dataIndex : 'weekday3',
41745             header : 'Wednesday',
41746             renderer : cellRender
41747         },
41748         {
41749             xtype: 'ColumnModel',
41750             xns: Roo.grid,
41751             dataIndex : 'weekday4',
41752             header : 'Thursday',
41753             renderer : cellRender
41754         },
41755         {
41756             xtype: 'ColumnModel',
41757             xns: Roo.grid,
41758             dataIndex : 'weekday5',
41759             header : 'Friday',
41760             renderer : cellRender
41761         },
41762         {
41763             xtype: 'ColumnModel',
41764             xns: Roo.grid,
41765             dataIndex : 'weekday6',
41766             header : 'Saturday',
41767             renderer : cellRender
41768         }
41769     ]);
41770     this.cm = this.colModel;
41771     this.cm.xmodule = this.xmodule || false;
41772  
41773         
41774           
41775     //this.selModel = new Roo.grid.CellSelectionModel();
41776     //this.sm = this.selModel;
41777     //this.selModel.init(this);
41778     
41779     
41780     if(this.width){
41781         this.container.setWidth(this.width);
41782     }
41783
41784     if(this.height){
41785         this.container.setHeight(this.height);
41786     }
41787     /** @private */
41788         this.addEvents({
41789         // raw events
41790         /**
41791          * @event click
41792          * The raw click event for the entire grid.
41793          * @param {Roo.EventObject} e
41794          */
41795         "click" : true,
41796         /**
41797          * @event dblclick
41798          * The raw dblclick event for the entire grid.
41799          * @param {Roo.EventObject} e
41800          */
41801         "dblclick" : true,
41802         /**
41803          * @event contextmenu
41804          * The raw contextmenu event for the entire grid.
41805          * @param {Roo.EventObject} e
41806          */
41807         "contextmenu" : true,
41808         /**
41809          * @event mousedown
41810          * The raw mousedown event for the entire grid.
41811          * @param {Roo.EventObject} e
41812          */
41813         "mousedown" : true,
41814         /**
41815          * @event mouseup
41816          * The raw mouseup event for the entire grid.
41817          * @param {Roo.EventObject} e
41818          */
41819         "mouseup" : true,
41820         /**
41821          * @event mouseover
41822          * The raw mouseover event for the entire grid.
41823          * @param {Roo.EventObject} e
41824          */
41825         "mouseover" : true,
41826         /**
41827          * @event mouseout
41828          * The raw mouseout event for the entire grid.
41829          * @param {Roo.EventObject} e
41830          */
41831         "mouseout" : true,
41832         /**
41833          * @event keypress
41834          * The raw keypress event for the entire grid.
41835          * @param {Roo.EventObject} e
41836          */
41837         "keypress" : true,
41838         /**
41839          * @event keydown
41840          * The raw keydown event for the entire grid.
41841          * @param {Roo.EventObject} e
41842          */
41843         "keydown" : true,
41844
41845         // custom events
41846
41847         /**
41848          * @event cellclick
41849          * Fires when a cell is clicked
41850          * @param {Grid} this
41851          * @param {Number} rowIndex
41852          * @param {Number} columnIndex
41853          * @param {Roo.EventObject} e
41854          */
41855         "cellclick" : true,
41856         /**
41857          * @event celldblclick
41858          * Fires when a cell is double clicked
41859          * @param {Grid} this
41860          * @param {Number} rowIndex
41861          * @param {Number} columnIndex
41862          * @param {Roo.EventObject} e
41863          */
41864         "celldblclick" : true,
41865         /**
41866          * @event rowclick
41867          * Fires when a row is clicked
41868          * @param {Grid} this
41869          * @param {Number} rowIndex
41870          * @param {Roo.EventObject} e
41871          */
41872         "rowclick" : true,
41873         /**
41874          * @event rowdblclick
41875          * Fires when a row is double clicked
41876          * @param {Grid} this
41877          * @param {Number} rowIndex
41878          * @param {Roo.EventObject} e
41879          */
41880         "rowdblclick" : true,
41881         /**
41882          * @event headerclick
41883          * Fires when a header is clicked
41884          * @param {Grid} this
41885          * @param {Number} columnIndex
41886          * @param {Roo.EventObject} e
41887          */
41888         "headerclick" : true,
41889         /**
41890          * @event headerdblclick
41891          * Fires when a header cell is double clicked
41892          * @param {Grid} this
41893          * @param {Number} columnIndex
41894          * @param {Roo.EventObject} e
41895          */
41896         "headerdblclick" : true,
41897         /**
41898          * @event rowcontextmenu
41899          * Fires when a row is right clicked
41900          * @param {Grid} this
41901          * @param {Number} rowIndex
41902          * @param {Roo.EventObject} e
41903          */
41904         "rowcontextmenu" : true,
41905         /**
41906          * @event cellcontextmenu
41907          * Fires when a cell is right clicked
41908          * @param {Grid} this
41909          * @param {Number} rowIndex
41910          * @param {Number} cellIndex
41911          * @param {Roo.EventObject} e
41912          */
41913          "cellcontextmenu" : true,
41914         /**
41915          * @event headercontextmenu
41916          * Fires when a header is right clicked
41917          * @param {Grid} this
41918          * @param {Number} columnIndex
41919          * @param {Roo.EventObject} e
41920          */
41921         "headercontextmenu" : true,
41922         /**
41923          * @event bodyscroll
41924          * Fires when the body element is scrolled
41925          * @param {Number} scrollLeft
41926          * @param {Number} scrollTop
41927          */
41928         "bodyscroll" : true,
41929         /**
41930          * @event columnresize
41931          * Fires when the user resizes a column
41932          * @param {Number} columnIndex
41933          * @param {Number} newSize
41934          */
41935         "columnresize" : true,
41936         /**
41937          * @event columnmove
41938          * Fires when the user moves a column
41939          * @param {Number} oldIndex
41940          * @param {Number} newIndex
41941          */
41942         "columnmove" : true,
41943         /**
41944          * @event startdrag
41945          * Fires when row(s) start being dragged
41946          * @param {Grid} this
41947          * @param {Roo.GridDD} dd The drag drop object
41948          * @param {event} e The raw browser event
41949          */
41950         "startdrag" : true,
41951         /**
41952          * @event enddrag
41953          * Fires when a drag operation is complete
41954          * @param {Grid} this
41955          * @param {Roo.GridDD} dd The drag drop object
41956          * @param {event} e The raw browser event
41957          */
41958         "enddrag" : true,
41959         /**
41960          * @event dragdrop
41961          * Fires when dragged row(s) are dropped on a valid DD target
41962          * @param {Grid} this
41963          * @param {Roo.GridDD} dd The drag drop object
41964          * @param {String} targetId The target drag drop object
41965          * @param {event} e The raw browser event
41966          */
41967         "dragdrop" : true,
41968         /**
41969          * @event dragover
41970          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41971          * @param {Grid} this
41972          * @param {Roo.GridDD} dd The drag drop object
41973          * @param {String} targetId The target drag drop object
41974          * @param {event} e The raw browser event
41975          */
41976         "dragover" : true,
41977         /**
41978          * @event dragenter
41979          *  Fires when the dragged row(s) first cross another DD target while being dragged
41980          * @param {Grid} this
41981          * @param {Roo.GridDD} dd The drag drop object
41982          * @param {String} targetId The target drag drop object
41983          * @param {event} e The raw browser event
41984          */
41985         "dragenter" : true,
41986         /**
41987          * @event dragout
41988          * Fires when the dragged row(s) leave another DD target while being dragged
41989          * @param {Grid} this
41990          * @param {Roo.GridDD} dd The drag drop object
41991          * @param {String} targetId The target drag drop object
41992          * @param {event} e The raw browser event
41993          */
41994         "dragout" : true,
41995         /**
41996          * @event rowclass
41997          * Fires when a row is rendered, so you can change add a style to it.
41998          * @param {GridView} gridview   The grid view
41999          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
42000          */
42001         'rowclass' : true,
42002
42003         /**
42004          * @event render
42005          * Fires when the grid is rendered
42006          * @param {Grid} grid
42007          */
42008         'render' : true,
42009             /**
42010              * @event select
42011              * Fires when a date is selected
42012              * @param {DatePicker} this
42013              * @param {Date} date The selected date
42014              */
42015         'select': true,
42016         /**
42017              * @event monthchange
42018              * Fires when the displayed month changes 
42019              * @param {DatePicker} this
42020              * @param {Date} date The selected month
42021              */
42022         'monthchange': true,
42023         /**
42024              * @event evententer
42025              * Fires when mouse over an event
42026              * @param {Calendar} this
42027              * @param {event} Event
42028              */
42029         'evententer': true,
42030         /**
42031              * @event eventleave
42032              * Fires when the mouse leaves an
42033              * @param {Calendar} this
42034              * @param {event}
42035              */
42036         'eventleave': true,
42037         /**
42038              * @event eventclick
42039              * Fires when the mouse click an
42040              * @param {Calendar} this
42041              * @param {event}
42042              */
42043         'eventclick': true,
42044         /**
42045              * @event eventrender
42046              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42047              * @param {Calendar} this
42048              * @param {data} data to be modified
42049              */
42050         'eventrender': true
42051         
42052     });
42053
42054     Roo.grid.Grid.superclass.constructor.call(this);
42055     this.on('render', function() {
42056         this.view.el.addClass('x-grid-cal'); 
42057         
42058         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42059
42060     },this);
42061     
42062     if (!Roo.grid.Calendar.style) {
42063         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42064             
42065             
42066             '.x-grid-cal .x-grid-col' :  {
42067                 height: 'auto !important',
42068                 'vertical-align': 'top'
42069             },
42070             '.x-grid-cal  .fc-event-hori' : {
42071                 height: '14px'
42072             }
42073              
42074             
42075         }, Roo.id());
42076     }
42077
42078     
42079     
42080 };
42081 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42082     /**
42083      * @cfg {Store} eventStore The store that loads events.
42084      */
42085     eventStore : 25,
42086
42087      
42088     activeDate : false,
42089     startDay : 0,
42090     autoWidth : true,
42091     monitorWindowResize : false,
42092
42093     
42094     resizeColumns : function() {
42095         var col = (this.view.el.getWidth() / 7) - 3;
42096         // loop through cols, and setWidth
42097         for(var i =0 ; i < 7 ; i++){
42098             this.cm.setColumnWidth(i, col);
42099         }
42100     },
42101      setDate :function(date) {
42102         
42103         Roo.log('setDate?');
42104         
42105         this.resizeColumns();
42106         var vd = this.activeDate;
42107         this.activeDate = date;
42108 //        if(vd && this.el){
42109 //            var t = date.getTime();
42110 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42111 //                Roo.log('using add remove');
42112 //                
42113 //                this.fireEvent('monthchange', this, date);
42114 //                
42115 //                this.cells.removeClass("fc-state-highlight");
42116 //                this.cells.each(function(c){
42117 //                   if(c.dateValue == t){
42118 //                       c.addClass("fc-state-highlight");
42119 //                       setTimeout(function(){
42120 //                            try{c.dom.firstChild.focus();}catch(e){}
42121 //                       }, 50);
42122 //                       return false;
42123 //                   }
42124 //                   return true;
42125 //                });
42126 //                return;
42127 //            }
42128 //        }
42129         
42130         var days = date.getDaysInMonth();
42131         
42132         var firstOfMonth = date.getFirstDateOfMonth();
42133         var startingPos = firstOfMonth.getDay()-this.startDay;
42134         
42135         if(startingPos < this.startDay){
42136             startingPos += 7;
42137         }
42138         
42139         var pm = date.add(Date.MONTH, -1);
42140         var prevStart = pm.getDaysInMonth()-startingPos;
42141 //        
42142         
42143         
42144         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42145         
42146         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42147         //this.cells.addClassOnOver('fc-state-hover');
42148         
42149         var cells = this.cells.elements;
42150         var textEls = this.textNodes;
42151         
42152         //Roo.each(cells, function(cell){
42153         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42154         //});
42155         
42156         days += startingPos;
42157
42158         // convert everything to numbers so it's fast
42159         var day = 86400000;
42160         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42161         //Roo.log(d);
42162         //Roo.log(pm);
42163         //Roo.log(prevStart);
42164         
42165         var today = new Date().clearTime().getTime();
42166         var sel = date.clearTime().getTime();
42167         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42168         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42169         var ddMatch = this.disabledDatesRE;
42170         var ddText = this.disabledDatesText;
42171         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42172         var ddaysText = this.disabledDaysText;
42173         var format = this.format;
42174         
42175         var setCellClass = function(cal, cell){
42176             
42177             //Roo.log('set Cell Class');
42178             cell.title = "";
42179             var t = d.getTime();
42180             
42181             //Roo.log(d);
42182             
42183             
42184             cell.dateValue = t;
42185             if(t == today){
42186                 cell.className += " fc-today";
42187                 cell.className += " fc-state-highlight";
42188                 cell.title = cal.todayText;
42189             }
42190             if(t == sel){
42191                 // disable highlight in other month..
42192                 cell.className += " fc-state-highlight";
42193                 
42194             }
42195             // disabling
42196             if(t < min) {
42197                 //cell.className = " fc-state-disabled";
42198                 cell.title = cal.minText;
42199                 return;
42200             }
42201             if(t > max) {
42202                 //cell.className = " fc-state-disabled";
42203                 cell.title = cal.maxText;
42204                 return;
42205             }
42206             if(ddays){
42207                 if(ddays.indexOf(d.getDay()) != -1){
42208                     // cell.title = ddaysText;
42209                    // cell.className = " fc-state-disabled";
42210                 }
42211             }
42212             if(ddMatch && format){
42213                 var fvalue = d.dateFormat(format);
42214                 if(ddMatch.test(fvalue)){
42215                     cell.title = ddText.replace("%0", fvalue);
42216                    cell.className = " fc-state-disabled";
42217                 }
42218             }
42219             
42220             if (!cell.initialClassName) {
42221                 cell.initialClassName = cell.dom.className;
42222             }
42223             
42224             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
42225         };
42226
42227         var i = 0;
42228         
42229         for(; i < startingPos; i++) {
42230             cells[i].dayName =  (++prevStart);
42231             Roo.log(textEls[i]);
42232             d.setDate(d.getDate()+1);
42233             
42234             //cells[i].className = "fc-past fc-other-month";
42235             setCellClass(this, cells[i]);
42236         }
42237         
42238         var intDay = 0;
42239         
42240         for(; i < days; i++){
42241             intDay = i - startingPos + 1;
42242             cells[i].dayName =  (intDay);
42243             d.setDate(d.getDate()+1);
42244             
42245             cells[i].className = ''; // "x-date-active";
42246             setCellClass(this, cells[i]);
42247         }
42248         var extraDays = 0;
42249         
42250         for(; i < 42; i++) {
42251             //textEls[i].innerHTML = (++extraDays);
42252             
42253             d.setDate(d.getDate()+1);
42254             cells[i].dayName = (++extraDays);
42255             cells[i].className = "fc-future fc-other-month";
42256             setCellClass(this, cells[i]);
42257         }
42258         
42259         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42260         
42261         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42262         
42263         // this will cause all the cells to mis
42264         var rows= [];
42265         var i =0;
42266         for (var r = 0;r < 6;r++) {
42267             for (var c =0;c < 7;c++) {
42268                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42269             }    
42270         }
42271         
42272         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42273         for(i=0;i<cells.length;i++) {
42274             
42275             this.cells.elements[i].dayName = cells[i].dayName ;
42276             this.cells.elements[i].className = cells[i].className;
42277             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42278             this.cells.elements[i].title = cells[i].title ;
42279             this.cells.elements[i].dateValue = cells[i].dateValue ;
42280         }
42281         
42282         
42283         
42284         
42285         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42286         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42287         
42288         ////if(totalRows != 6){
42289             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42290            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42291        // }
42292         
42293         this.fireEvent('monthchange', this, date);
42294         
42295         
42296     },
42297  /**
42298      * Returns the grid's SelectionModel.
42299      * @return {SelectionModel}
42300      */
42301     getSelectionModel : function(){
42302         if(!this.selModel){
42303             this.selModel = new Roo.grid.CellSelectionModel();
42304         }
42305         return this.selModel;
42306     },
42307
42308     load: function() {
42309         this.eventStore.load()
42310         
42311         
42312         
42313     },
42314     
42315     findCell : function(dt) {
42316         dt = dt.clearTime().getTime();
42317         var ret = false;
42318         this.cells.each(function(c){
42319             //Roo.log("check " +c.dateValue + '?=' + dt);
42320             if(c.dateValue == dt){
42321                 ret = c;
42322                 return false;
42323             }
42324             return true;
42325         });
42326         
42327         return ret;
42328     },
42329     
42330     findCells : function(rec) {
42331         var s = rec.data.start_dt.clone().clearTime().getTime();
42332        // Roo.log(s);
42333         var e= rec.data.end_dt.clone().clearTime().getTime();
42334        // Roo.log(e);
42335         var ret = [];
42336         this.cells.each(function(c){
42337              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42338             
42339             if(c.dateValue > e){
42340                 return ;
42341             }
42342             if(c.dateValue < s){
42343                 return ;
42344             }
42345             ret.push(c);
42346         });
42347         
42348         return ret;    
42349     },
42350     
42351     findBestRow: function(cells)
42352     {
42353         var ret = 0;
42354         
42355         for (var i =0 ; i < cells.length;i++) {
42356             ret  = Math.max(cells[i].rows || 0,ret);
42357         }
42358         return ret;
42359         
42360     },
42361     
42362     
42363     addItem : function(rec)
42364     {
42365         // look for vertical location slot in
42366         var cells = this.findCells(rec);
42367         
42368         rec.row = this.findBestRow(cells);
42369         
42370         // work out the location.
42371         
42372         var crow = false;
42373         var rows = [];
42374         for(var i =0; i < cells.length; i++) {
42375             if (!crow) {
42376                 crow = {
42377                     start : cells[i],
42378                     end :  cells[i]
42379                 };
42380                 continue;
42381             }
42382             if (crow.start.getY() == cells[i].getY()) {
42383                 // on same row.
42384                 crow.end = cells[i];
42385                 continue;
42386             }
42387             // different row.
42388             rows.push(crow);
42389             crow = {
42390                 start: cells[i],
42391                 end : cells[i]
42392             };
42393             
42394         }
42395         
42396         rows.push(crow);
42397         rec.els = [];
42398         rec.rows = rows;
42399         rec.cells = cells;
42400         for (var i = 0; i < cells.length;i++) {
42401             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42402             
42403         }
42404         
42405         
42406     },
42407     
42408     clearEvents: function() {
42409         
42410         if (!this.eventStore.getCount()) {
42411             return;
42412         }
42413         // reset number of rows in cells.
42414         Roo.each(this.cells.elements, function(c){
42415             c.rows = 0;
42416         });
42417         
42418         this.eventStore.each(function(e) {
42419             this.clearEvent(e);
42420         },this);
42421         
42422     },
42423     
42424     clearEvent : function(ev)
42425     {
42426         if (ev.els) {
42427             Roo.each(ev.els, function(el) {
42428                 el.un('mouseenter' ,this.onEventEnter, this);
42429                 el.un('mouseleave' ,this.onEventLeave, this);
42430                 el.remove();
42431             },this);
42432             ev.els = [];
42433         }
42434     },
42435     
42436     
42437     renderEvent : function(ev,ctr) {
42438         if (!ctr) {
42439              ctr = this.view.el.select('.fc-event-container',true).first();
42440         }
42441         
42442          
42443         this.clearEvent(ev);
42444             //code
42445        
42446         
42447         
42448         ev.els = [];
42449         var cells = ev.cells;
42450         var rows = ev.rows;
42451         this.fireEvent('eventrender', this, ev);
42452         
42453         for(var i =0; i < rows.length; i++) {
42454             
42455             cls = '';
42456             if (i == 0) {
42457                 cls += ' fc-event-start';
42458             }
42459             if ((i+1) == rows.length) {
42460                 cls += ' fc-event-end';
42461             }
42462             
42463             //Roo.log(ev.data);
42464             // how many rows should it span..
42465             var cg = this.eventTmpl.append(ctr,Roo.apply({
42466                 fccls : cls
42467                 
42468             }, ev.data) , true);
42469             
42470             
42471             cg.on('mouseenter' ,this.onEventEnter, this, ev);
42472             cg.on('mouseleave' ,this.onEventLeave, this, ev);
42473             cg.on('click', this.onEventClick, this, ev);
42474             
42475             ev.els.push(cg);
42476             
42477             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42478             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42479             //Roo.log(cg);
42480              
42481             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
42482             cg.setWidth(ebox.right - sbox.x -2);
42483         }
42484     },
42485     
42486     renderEvents: function()
42487     {   
42488         // first make sure there is enough space..
42489         
42490         if (!this.eventTmpl) {
42491             this.eventTmpl = new Roo.Template(
42492                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
42493                     '<div class="fc-event-inner">' +
42494                         '<span class="fc-event-time">{time}</span>' +
42495                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42496                     '</div>' +
42497                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
42498                 '</div>'
42499             );
42500                 
42501         }
42502                
42503         
42504         
42505         this.cells.each(function(c) {
42506             //Roo.log(c.select('.fc-day-content div',true).first());
42507             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42508         });
42509         
42510         var ctr = this.view.el.select('.fc-event-container',true).first();
42511         
42512         var cls;
42513         this.eventStore.each(function(ev){
42514             
42515             this.renderEvent(ev);
42516              
42517              
42518         }, this);
42519         this.view.layout();
42520         
42521     },
42522     
42523     onEventEnter: function (e, el,event,d) {
42524         this.fireEvent('evententer', this, el, event);
42525     },
42526     
42527     onEventLeave: function (e, el,event,d) {
42528         this.fireEvent('eventleave', this, el, event);
42529     },
42530     
42531     onEventClick: function (e, el,event,d) {
42532         this.fireEvent('eventclick', this, el, event);
42533     },
42534     
42535     onMonthChange: function () {
42536         this.store.load();
42537     },
42538     
42539     onLoad: function () {
42540         
42541         //Roo.log('calendar onload');
42542 //         
42543         if(this.eventStore.getCount() > 0){
42544             
42545            
42546             
42547             this.eventStore.each(function(d){
42548                 
42549                 
42550                 // FIXME..
42551                 var add =   d.data;
42552                 if (typeof(add.end_dt) == 'undefined')  {
42553                     Roo.log("Missing End time in calendar data: ");
42554                     Roo.log(d);
42555                     return;
42556                 }
42557                 if (typeof(add.start_dt) == 'undefined')  {
42558                     Roo.log("Missing Start time in calendar data: ");
42559                     Roo.log(d);
42560                     return;
42561                 }
42562                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42563                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42564                 add.id = add.id || d.id;
42565                 add.title = add.title || '??';
42566                 
42567                 this.addItem(d);
42568                 
42569              
42570             },this);
42571         }
42572         
42573         this.renderEvents();
42574     }
42575     
42576
42577 });
42578 /*
42579  grid : {
42580                 xtype: 'Grid',
42581                 xns: Roo.grid,
42582                 listeners : {
42583                     render : function ()
42584                     {
42585                         _this.grid = this;
42586                         
42587                         if (!this.view.el.hasClass('course-timesheet')) {
42588                             this.view.el.addClass('course-timesheet');
42589                         }
42590                         if (this.tsStyle) {
42591                             this.ds.load({});
42592                             return; 
42593                         }
42594                         Roo.log('width');
42595                         Roo.log(_this.grid.view.el.getWidth());
42596                         
42597                         
42598                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
42599                             '.course-timesheet .x-grid-row' : {
42600                                 height: '80px'
42601                             },
42602                             '.x-grid-row td' : {
42603                                 'vertical-align' : 0
42604                             },
42605                             '.course-edit-link' : {
42606                                 'color' : 'blue',
42607                                 'text-overflow' : 'ellipsis',
42608                                 'overflow' : 'hidden',
42609                                 'white-space' : 'nowrap',
42610                                 'cursor' : 'pointer'
42611                             },
42612                             '.sub-link' : {
42613                                 'color' : 'green'
42614                             },
42615                             '.de-act-sup-link' : {
42616                                 'color' : 'purple',
42617                                 'text-decoration' : 'line-through'
42618                             },
42619                             '.de-act-link' : {
42620                                 'color' : 'red',
42621                                 'text-decoration' : 'line-through'
42622                             },
42623                             '.course-timesheet .course-highlight' : {
42624                                 'border-top-style': 'dashed !important',
42625                                 'border-bottom-bottom': 'dashed !important'
42626                             },
42627                             '.course-timesheet .course-item' : {
42628                                 'font-family'   : 'tahoma, arial, helvetica',
42629                                 'font-size'     : '11px',
42630                                 'overflow'      : 'hidden',
42631                                 'padding-left'  : '10px',
42632                                 'padding-right' : '10px',
42633                                 'padding-top' : '10px' 
42634                             }
42635                             
42636                         }, Roo.id());
42637                                 this.ds.load({});
42638                     }
42639                 },
42640                 autoWidth : true,
42641                 monitorWindowResize : false,
42642                 cellrenderer : function(v,x,r)
42643                 {
42644                     return v;
42645                 },
42646                 sm : {
42647                     xtype: 'CellSelectionModel',
42648                     xns: Roo.grid
42649                 },
42650                 dataSource : {
42651                     xtype: 'Store',
42652                     xns: Roo.data,
42653                     listeners : {
42654                         beforeload : function (_self, options)
42655                         {
42656                             options.params = options.params || {};
42657                             options.params._month = _this.monthField.getValue();
42658                             options.params.limit = 9999;
42659                             options.params['sort'] = 'when_dt';    
42660                             options.params['dir'] = 'ASC';    
42661                             this.proxy.loadResponse = this.loadResponse;
42662                             Roo.log("load?");
42663                             //this.addColumns();
42664                         },
42665                         load : function (_self, records, options)
42666                         {
42667                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42668                                 // if you click on the translation.. you can edit it...
42669                                 var el = Roo.get(this);
42670                                 var id = el.dom.getAttribute('data-id');
42671                                 var d = el.dom.getAttribute('data-date');
42672                                 var t = el.dom.getAttribute('data-time');
42673                                 //var id = this.child('span').dom.textContent;
42674                                 
42675                                 //Roo.log(this);
42676                                 Pman.Dialog.CourseCalendar.show({
42677                                     id : id,
42678                                     when_d : d,
42679                                     when_t : t,
42680                                     productitem_active : id ? 1 : 0
42681                                 }, function() {
42682                                     _this.grid.ds.load({});
42683                                 });
42684                            
42685                            });
42686                            
42687                            _this.panel.fireEvent('resize', [ '', '' ]);
42688                         }
42689                     },
42690                     loadResponse : function(o, success, response){
42691                             // this is overridden on before load..
42692                             
42693                             Roo.log("our code?");       
42694                             //Roo.log(success);
42695                             //Roo.log(response)
42696                             delete this.activeRequest;
42697                             if(!success){
42698                                 this.fireEvent("loadexception", this, o, response);
42699                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42700                                 return;
42701                             }
42702                             var result;
42703                             try {
42704                                 result = o.reader.read(response);
42705                             }catch(e){
42706                                 Roo.log("load exception?");
42707                                 this.fireEvent("loadexception", this, o, response, e);
42708                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42709                                 return;
42710                             }
42711                             Roo.log("ready...");        
42712                             // loop through result.records;
42713                             // and set this.tdate[date] = [] << array of records..
42714                             _this.tdata  = {};
42715                             Roo.each(result.records, function(r){
42716                                 //Roo.log(r.data);
42717                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42718                                     _this.tdata[r.data.when_dt.format('j')] = [];
42719                                 }
42720                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42721                             });
42722                             
42723                             //Roo.log(_this.tdata);
42724                             
42725                             result.records = [];
42726                             result.totalRecords = 6;
42727                     
42728                             // let's generate some duumy records for the rows.
42729                             //var st = _this.dateField.getValue();
42730                             
42731                             // work out monday..
42732                             //st = st.add(Date.DAY, -1 * st.format('w'));
42733                             
42734                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42735                             
42736                             var firstOfMonth = date.getFirstDayOfMonth();
42737                             var days = date.getDaysInMonth();
42738                             var d = 1;
42739                             var firstAdded = false;
42740                             for (var i = 0; i < result.totalRecords ; i++) {
42741                                 //var d= st.add(Date.DAY, i);
42742                                 var row = {};
42743                                 var added = 0;
42744                                 for(var w = 0 ; w < 7 ; w++){
42745                                     if(!firstAdded && firstOfMonth != w){
42746                                         continue;
42747                                     }
42748                                     if(d > days){
42749                                         continue;
42750                                     }
42751                                     firstAdded = true;
42752                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
42753                                     row['weekday'+w] = String.format(
42754                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
42755                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42756                                                     d,
42757                                                     date.format('Y-m-')+dd
42758                                                 );
42759                                     added++;
42760                                     if(typeof(_this.tdata[d]) != 'undefined'){
42761                                         Roo.each(_this.tdata[d], function(r){
42762                                             var is_sub = '';
42763                                             var deactive = '';
42764                                             var id = r.id;
42765                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
42766                                             if(r.parent_id*1>0){
42767                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
42768                                                 id = r.parent_id;
42769                                             }
42770                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
42771                                                 deactive = 'de-act-link';
42772                                             }
42773                                             
42774                                             row['weekday'+w] += String.format(
42775                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42776                                                     id, //0
42777                                                     r.product_id_name, //1
42778                                                     r.when_dt.format('h:ia'), //2
42779                                                     is_sub, //3
42780                                                     deactive, //4
42781                                                     desc // 5
42782                                             );
42783                                         });
42784                                     }
42785                                     d++;
42786                                 }
42787                                 
42788                                 // only do this if something added..
42789                                 if(added > 0){ 
42790                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
42791                                 }
42792                                 
42793                                 
42794                                 // push it twice. (second one with an hour..
42795                                 
42796                             }
42797                             //Roo.log(result);
42798                             this.fireEvent("load", this, o, o.request.arg);
42799                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42800                         },
42801                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42802                     proxy : {
42803                         xtype: 'HttpProxy',
42804                         xns: Roo.data,
42805                         method : 'GET',
42806                         url : baseURL + '/Roo/Shop_course.php'
42807                     },
42808                     reader : {
42809                         xtype: 'JsonReader',
42810                         xns: Roo.data,
42811                         id : 'id',
42812                         fields : [
42813                             {
42814                                 'name': 'id',
42815                                 'type': 'int'
42816                             },
42817                             {
42818                                 'name': 'when_dt',
42819                                 'type': 'string'
42820                             },
42821                             {
42822                                 'name': 'end_dt',
42823                                 'type': 'string'
42824                             },
42825                             {
42826                                 'name': 'parent_id',
42827                                 'type': 'int'
42828                             },
42829                             {
42830                                 'name': 'product_id',
42831                                 'type': 'int'
42832                             },
42833                             {
42834                                 'name': 'productitem_id',
42835                                 'type': 'int'
42836                             },
42837                             {
42838                                 'name': 'guid',
42839                                 'type': 'int'
42840                             }
42841                         ]
42842                     }
42843                 },
42844                 toolbar : {
42845                     xtype: 'Toolbar',
42846                     xns: Roo,
42847                     items : [
42848                         {
42849                             xtype: 'Button',
42850                             xns: Roo.Toolbar,
42851                             listeners : {
42852                                 click : function (_self, e)
42853                                 {
42854                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42855                                     sd.setMonth(sd.getMonth()-1);
42856                                     _this.monthField.setValue(sd.format('Y-m-d'));
42857                                     _this.grid.ds.load({});
42858                                 }
42859                             },
42860                             text : "Back"
42861                         },
42862                         {
42863                             xtype: 'Separator',
42864                             xns: Roo.Toolbar
42865                         },
42866                         {
42867                             xtype: 'MonthField',
42868                             xns: Roo.form,
42869                             listeners : {
42870                                 render : function (_self)
42871                                 {
42872                                     _this.monthField = _self;
42873                                    // _this.monthField.set  today
42874                                 },
42875                                 select : function (combo, date)
42876                                 {
42877                                     _this.grid.ds.load({});
42878                                 }
42879                             },
42880                             value : (function() { return new Date(); })()
42881                         },
42882                         {
42883                             xtype: 'Separator',
42884                             xns: Roo.Toolbar
42885                         },
42886                         {
42887                             xtype: 'TextItem',
42888                             xns: Roo.Toolbar,
42889                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42890                         },
42891                         {
42892                             xtype: 'Fill',
42893                             xns: Roo.Toolbar
42894                         },
42895                         {
42896                             xtype: 'Button',
42897                             xns: Roo.Toolbar,
42898                             listeners : {
42899                                 click : function (_self, e)
42900                                 {
42901                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42902                                     sd.setMonth(sd.getMonth()+1);
42903                                     _this.monthField.setValue(sd.format('Y-m-d'));
42904                                     _this.grid.ds.load({});
42905                                 }
42906                             },
42907                             text : "Next"
42908                         }
42909                     ]
42910                 },
42911                  
42912             }
42913         };
42914         
42915         *//*
42916  * Based on:
42917  * Ext JS Library 1.1.1
42918  * Copyright(c) 2006-2007, Ext JS, LLC.
42919  *
42920  * Originally Released Under LGPL - original licence link has changed is not relivant.
42921  *
42922  * Fork - LGPL
42923  * <script type="text/javascript">
42924  */
42925  
42926 /**
42927  * @class Roo.LoadMask
42928  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42929  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42930  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42931  * element's UpdateManager load indicator and will be destroyed after the initial load.
42932  * @constructor
42933  * Create a new LoadMask
42934  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42935  * @param {Object} config The config object
42936  */
42937 Roo.LoadMask = function(el, config){
42938     this.el = Roo.get(el);
42939     Roo.apply(this, config);
42940     if(this.store){
42941         this.store.on('beforeload', this.onBeforeLoad, this);
42942         this.store.on('load', this.onLoad, this);
42943         this.store.on('loadexception', this.onLoadException, this);
42944         this.removeMask = false;
42945     }else{
42946         var um = this.el.getUpdateManager();
42947         um.showLoadIndicator = false; // disable the default indicator
42948         um.on('beforeupdate', this.onBeforeLoad, this);
42949         um.on('update', this.onLoad, this);
42950         um.on('failure', this.onLoad, this);
42951         this.removeMask = true;
42952     }
42953 };
42954
42955 Roo.LoadMask.prototype = {
42956     /**
42957      * @cfg {Boolean} removeMask
42958      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42959      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42960      */
42961     removeMask : false,
42962     /**
42963      * @cfg {String} msg
42964      * The text to display in a centered loading message box (defaults to 'Loading...')
42965      */
42966     msg : 'Loading...',
42967     /**
42968      * @cfg {String} msgCls
42969      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42970      */
42971     msgCls : 'x-mask-loading',
42972
42973     /**
42974      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42975      * @type Boolean
42976      */
42977     disabled: false,
42978
42979     /**
42980      * Disables the mask to prevent it from being displayed
42981      */
42982     disable : function(){
42983        this.disabled = true;
42984     },
42985
42986     /**
42987      * Enables the mask so that it can be displayed
42988      */
42989     enable : function(){
42990         this.disabled = false;
42991     },
42992     
42993     onLoadException : function()
42994     {
42995         Roo.log(arguments);
42996         
42997         if (typeof(arguments[3]) != 'undefined') {
42998             Roo.MessageBox.alert("Error loading",arguments[3]);
42999         } 
43000         /*
43001         try {
43002             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43003                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43004             }   
43005         } catch(e) {
43006             
43007         }
43008         */
43009     
43010         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43011     },
43012     // private
43013     onLoad : function()
43014     {
43015         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43016     },
43017
43018     // private
43019     onBeforeLoad : function(){
43020         if(!this.disabled){
43021             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
43022         }
43023     },
43024
43025     // private
43026     destroy : function(){
43027         if(this.store){
43028             this.store.un('beforeload', this.onBeforeLoad, this);
43029             this.store.un('load', this.onLoad, this);
43030             this.store.un('loadexception', this.onLoadException, this);
43031         }else{
43032             var um = this.el.getUpdateManager();
43033             um.un('beforeupdate', this.onBeforeLoad, this);
43034             um.un('update', this.onLoad, this);
43035             um.un('failure', this.onLoad, this);
43036         }
43037     }
43038 };/*
43039  * Based on:
43040  * Ext JS Library 1.1.1
43041  * Copyright(c) 2006-2007, Ext JS, LLC.
43042  *
43043  * Originally Released Under LGPL - original licence link has changed is not relivant.
43044  *
43045  * Fork - LGPL
43046  * <script type="text/javascript">
43047  */
43048
43049
43050 /**
43051  * @class Roo.XTemplate
43052  * @extends Roo.Template
43053  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43054 <pre><code>
43055 var t = new Roo.XTemplate(
43056         '&lt;select name="{name}"&gt;',
43057                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
43058         '&lt;/select&gt;'
43059 );
43060  
43061 // then append, applying the master template values
43062  </code></pre>
43063  *
43064  * Supported features:
43065  *
43066  *  Tags:
43067
43068 <pre><code>
43069       {a_variable} - output encoded.
43070       {a_variable.format:("Y-m-d")} - call a method on the variable
43071       {a_variable:raw} - unencoded output
43072       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43073       {a_variable:this.method_on_template(...)} - call a method on the template object.
43074  
43075 </code></pre>
43076  *  The tpl tag:
43077 <pre><code>
43078         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
43079         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
43080         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
43081         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
43082   
43083         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
43084         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
43085 </code></pre>
43086  *      
43087  */
43088 Roo.XTemplate = function()
43089 {
43090     Roo.XTemplate.superclass.constructor.apply(this, arguments);
43091     if (this.html) {
43092         this.compile();
43093     }
43094 };
43095
43096
43097 Roo.extend(Roo.XTemplate, Roo.Template, {
43098
43099     /**
43100      * The various sub templates
43101      */
43102     tpls : false,
43103     /**
43104      *
43105      * basic tag replacing syntax
43106      * WORD:WORD()
43107      *
43108      * // you can fake an object call by doing this
43109      *  x.t:(test,tesT) 
43110      * 
43111      */
43112     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43113
43114     /**
43115      * compile the template
43116      *
43117      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43118      *
43119      */
43120     compile: function()
43121     {
43122         var s = this.html;
43123      
43124         s = ['<tpl>', s, '</tpl>'].join('');
43125     
43126         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43127             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43128             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
43129             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43130             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
43131             m,
43132             id     = 0,
43133             tpls   = [];
43134     
43135         while(true == !!(m = s.match(re))){
43136             var forMatch   = m[0].match(nameRe),
43137                 ifMatch   = m[0].match(ifRe),
43138                 execMatch   = m[0].match(execRe),
43139                 namedMatch   = m[0].match(namedRe),
43140                 
43141                 exp  = null, 
43142                 fn   = null,
43143                 exec = null,
43144                 name = forMatch && forMatch[1] ? forMatch[1] : '';
43145                 
43146             if (ifMatch) {
43147                 // if - puts fn into test..
43148                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43149                 if(exp){
43150                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43151                 }
43152             }
43153             
43154             if (execMatch) {
43155                 // exec - calls a function... returns empty if true is  returned.
43156                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43157                 if(exp){
43158                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43159                 }
43160             }
43161             
43162             
43163             if (name) {
43164                 // for = 
43165                 switch(name){
43166                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43167                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43168                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43169                 }
43170             }
43171             var uid = namedMatch ? namedMatch[1] : id;
43172             
43173             
43174             tpls.push({
43175                 id:     namedMatch ? namedMatch[1] : id,
43176                 target: name,
43177                 exec:   exec,
43178                 test:   fn,
43179                 body:   m[1] || ''
43180             });
43181             if (namedMatch) {
43182                 s = s.replace(m[0], '');
43183             } else { 
43184                 s = s.replace(m[0], '{xtpl'+ id + '}');
43185             }
43186             ++id;
43187         }
43188         this.tpls = [];
43189         for(var i = tpls.length-1; i >= 0; --i){
43190             this.compileTpl(tpls[i]);
43191             this.tpls[tpls[i].id] = tpls[i];
43192         }
43193         this.master = tpls[tpls.length-1];
43194         return this;
43195     },
43196     /**
43197      * same as applyTemplate, except it's done to one of the subTemplates
43198      * when using named templates, you can do:
43199      *
43200      * var str = pl.applySubTemplate('your-name', values);
43201      *
43202      * 
43203      * @param {Number} id of the template
43204      * @param {Object} values to apply to template
43205      * @param {Object} parent (normaly the instance of this object)
43206      */
43207     applySubTemplate : function(id, values, parent)
43208     {
43209         
43210         
43211         var t = this.tpls[id];
43212         
43213         
43214         try { 
43215             if(t.test && !t.test.call(this, values, parent)){
43216                 return '';
43217             }
43218         } catch(e) {
43219             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43220             Roo.log(e.toString());
43221             Roo.log(t.test);
43222             return ''
43223         }
43224         try { 
43225             
43226             if(t.exec && t.exec.call(this, values, parent)){
43227                 return '';
43228             }
43229         } catch(e) {
43230             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43231             Roo.log(e.toString());
43232             Roo.log(t.exec);
43233             return ''
43234         }
43235         try {
43236             var vs = t.target ? t.target.call(this, values, parent) : values;
43237             parent = t.target ? values : parent;
43238             if(t.target && vs instanceof Array){
43239                 var buf = [];
43240                 for(var i = 0, len = vs.length; i < len; i++){
43241                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
43242                 }
43243                 return buf.join('');
43244             }
43245             return t.compiled.call(this, vs, parent);
43246         } catch (e) {
43247             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43248             Roo.log(e.toString());
43249             Roo.log(t.compiled);
43250             return '';
43251         }
43252     },
43253
43254     compileTpl : function(tpl)
43255     {
43256         var fm = Roo.util.Format;
43257         var useF = this.disableFormats !== true;
43258         var sep = Roo.isGecko ? "+" : ",";
43259         var undef = function(str) {
43260             Roo.log("Property not found :"  + str);
43261             return '';
43262         };
43263         
43264         var fn = function(m, name, format, args)
43265         {
43266             //Roo.log(arguments);
43267             args = args ? args.replace(/\\'/g,"'") : args;
43268             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43269             if (typeof(format) == 'undefined') {
43270                 format= 'htmlEncode';
43271             }
43272             if (format == 'raw' ) {
43273                 format = false;
43274             }
43275             
43276             if(name.substr(0, 4) == 'xtpl'){
43277                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43278             }
43279             
43280             // build an array of options to determine if value is undefined..
43281             
43282             // basically get 'xxxx.yyyy' then do
43283             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43284             //    (function () { Roo.log("Property not found"); return ''; })() :
43285             //    ......
43286             
43287             var udef_ar = [];
43288             var lookfor = '';
43289             Roo.each(name.split('.'), function(st) {
43290                 lookfor += (lookfor.length ? '.': '') + st;
43291                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
43292             });
43293             
43294             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43295             
43296             
43297             if(format && useF){
43298                 
43299                 args = args ? ',' + args : "";
43300                  
43301                 if(format.substr(0, 5) != "this."){
43302                     format = "fm." + format + '(';
43303                 }else{
43304                     format = 'this.call("'+ format.substr(5) + '", ';
43305                     args = ", values";
43306                 }
43307                 
43308                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
43309             }
43310              
43311             if (args.length) {
43312                 // called with xxyx.yuu:(test,test)
43313                 // change to ()
43314                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
43315             }
43316             // raw.. - :raw modifier..
43317             return "'"+ sep + udef_st  + name + ")"+sep+"'";
43318             
43319         };
43320         var body;
43321         // branched to use + in gecko and [].join() in others
43322         if(Roo.isGecko){
43323             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
43324                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43325                     "';};};";
43326         }else{
43327             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
43328             body.push(tpl.body.replace(/(\r\n|\n)/g,
43329                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43330             body.push("'].join('');};};");
43331             body = body.join('');
43332         }
43333         
43334         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43335        
43336         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
43337         eval(body);
43338         
43339         return this;
43340     },
43341
43342     applyTemplate : function(values){
43343         return this.master.compiled.call(this, values, {});
43344         //var s = this.subs;
43345     },
43346
43347     apply : function(){
43348         return this.applyTemplate.apply(this, arguments);
43349     }
43350
43351  });
43352
43353 Roo.XTemplate.from = function(el){
43354     el = Roo.getDom(el);
43355     return new Roo.XTemplate(el.value || el.innerHTML);
43356 };