2c1025a41d13a226fb1804963d2119fea44b1147
[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} ret return data from JsonData.reader() - success, totalRecords, records
485          * @param {Object} opts - 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  * @extends Roo.data.DataProxy
1398  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1399  * to the Reader when its load method is called.
1400  * @constructor
1401  * @param {Object} config  A config object containing the objects needed for the Store to access data,
1402  */
1403 Roo.data.MemoryProxy = function(config){
1404     var data = config;
1405     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
1406         data = config.data;
1407     }
1408     Roo.data.MemoryProxy.superclass.constructor.call(this);
1409     this.data = data;
1410 };
1411
1412 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1413     
1414     /**
1415      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1416      */
1417     /**
1418      * Load data from the requested source (in this case an in-memory
1419      * data object passed to the constructor), read the data object into
1420      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1421      * process that block using the passed callback.
1422      * @param {Object} params This parameter is not used by the MemoryProxy class.
1423      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1424      * object into a block of Roo.data.Records.
1425      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1426      * The function must be passed <ul>
1427      * <li>The Record block object</li>
1428      * <li>The "arg" argument from the load function</li>
1429      * <li>A boolean success indicator</li>
1430      * </ul>
1431      * @param {Object} scope The scope in which to call the callback
1432      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1433      */
1434     load : function(params, reader, callback, scope, arg){
1435         params = params || {};
1436         var result;
1437         try {
1438             result = reader.readRecords(params.data ? params.data :this.data);
1439         }catch(e){
1440             this.fireEvent("loadexception", this, arg, null, e);
1441             callback.call(scope, null, arg, false);
1442             return;
1443         }
1444         callback.call(scope, result, arg, true);
1445     },
1446     
1447     // private
1448     update : function(params, records){
1449         
1450     }
1451 });/*
1452  * Based on:
1453  * Ext JS Library 1.1.1
1454  * Copyright(c) 2006-2007, Ext JS, LLC.
1455  *
1456  * Originally Released Under LGPL - original licence link has changed is not relivant.
1457  *
1458  * Fork - LGPL
1459  * <script type="text/javascript">
1460  */
1461 /**
1462  * @class Roo.data.HttpProxy
1463  * @extends Roo.data.DataProxy
1464  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1465  * configured to reference a certain URL.<br><br>
1466  * <p>
1467  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1468  * from which the running page was served.<br><br>
1469  * <p>
1470  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1471  * <p>
1472  * Be aware that to enable the browser to parse an XML document, the server must set
1473  * the Content-Type header in the HTTP response to "text/xml".
1474  * @constructor
1475  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1476  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1477  * will be used to make the request.
1478  */
1479 Roo.data.HttpProxy = function(conn){
1480     Roo.data.HttpProxy.superclass.constructor.call(this);
1481     // is conn a conn config or a real conn?
1482     this.conn = conn;
1483     this.useAjax = !conn || !conn.events;
1484   
1485 };
1486
1487 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1488     // thse are take from connection...
1489     
1490     /**
1491      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
1492      */
1493     /**
1494      * @cfg {Object} extraParams  An object containing properties which are used as
1495      * extra parameters to each request made by this object. (defaults to undefined)
1496      */
1497     /**
1498      * @cfg {Object} defaultHeaders   An object containing request headers which are added
1499      *  to each request made by this object. (defaults to undefined)
1500      */
1501     /**
1502      * @cfg {String} method (GET|POST)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1503      */
1504     /**
1505      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
1506      */
1507      /**
1508      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
1509      * @type Boolean
1510      */
1511   
1512
1513     /**
1514      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1515      * @type Boolean
1516      */
1517     /**
1518      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1519      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1520      * a finer-grained basis than the DataProxy events.
1521      */
1522     getConnection : function(){
1523         return this.useAjax ? Roo.Ajax : this.conn;
1524     },
1525
1526     /**
1527      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1528      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1529      * process that block using the passed callback.
1530      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1531      * for the request to the remote server.
1532      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1533      * object into a block of Roo.data.Records.
1534      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1535      * The function must be passed <ul>
1536      * <li>The Record block object</li>
1537      * <li>The "arg" argument from the load function</li>
1538      * <li>A boolean success indicator</li>
1539      * </ul>
1540      * @param {Object} scope The scope in which to call the callback
1541      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1542      */
1543     load : function(params, reader, callback, scope, arg){
1544         if(this.fireEvent("beforeload", this, params) !== false){
1545             var  o = {
1546                 params : params || {},
1547                 request: {
1548                     callback : callback,
1549                     scope : scope,
1550                     arg : arg
1551                 },
1552                 reader: reader,
1553                 callback : this.loadResponse,
1554                 scope: this
1555             };
1556             if(this.useAjax){
1557                 Roo.applyIf(o, this.conn);
1558                 if(this.activeRequest){
1559                     Roo.Ajax.abort(this.activeRequest);
1560                 }
1561                 this.activeRequest = Roo.Ajax.request(o);
1562             }else{
1563                 this.conn.request(o);
1564             }
1565         }else{
1566             callback.call(scope||this, null, arg, false);
1567         }
1568     },
1569
1570     // private
1571     loadResponse : function(o, success, response){
1572         delete this.activeRequest;
1573         if(!success){
1574             this.fireEvent("loadexception", this, o, response);
1575             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1576             return;
1577         }
1578         var result;
1579         try {
1580             result = o.reader.read(response);
1581         }catch(e){
1582             o.success = false;
1583             o.raw = { errorMsg : response.responseText };
1584             this.fireEvent("loadexception", this, o, response, e);
1585             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1586             return;
1587         }
1588         
1589         this.fireEvent("load", this, o, o.request.arg);
1590         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1591     },
1592
1593     // private
1594     update : function(dataSet){
1595
1596     },
1597
1598     // private
1599     updateResponse : function(dataSet){
1600
1601     }
1602 });/*
1603  * Based on:
1604  * Ext JS Library 1.1.1
1605  * Copyright(c) 2006-2007, Ext JS, LLC.
1606  *
1607  * Originally Released Under LGPL - original licence link has changed is not relivant.
1608  *
1609  * Fork - LGPL
1610  * <script type="text/javascript">
1611  */
1612
1613 /**
1614  * @class Roo.data.ScriptTagProxy
1615  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1616  * other than the originating domain of the running page.<br><br>
1617  * <p>
1618  * <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
1619  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1620  * <p>
1621  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1622  * source code that is used as the source inside a &lt;script> tag.<br><br>
1623  * <p>
1624  * In order for the browser to process the returned data, the server must wrap the data object
1625  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1626  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1627  * depending on whether the callback name was passed:
1628  * <p>
1629  * <pre><code>
1630 boolean scriptTag = false;
1631 String cb = request.getParameter("callback");
1632 if (cb != null) {
1633     scriptTag = true;
1634     response.setContentType("text/javascript");
1635 } else {
1636     response.setContentType("application/x-json");
1637 }
1638 Writer out = response.getWriter();
1639 if (scriptTag) {
1640     out.write(cb + "(");
1641 }
1642 out.print(dataBlock.toJsonString());
1643 if (scriptTag) {
1644     out.write(");");
1645 }
1646 </pre></code>
1647  *
1648  * @constructor
1649  * @param {Object} config A configuration object.
1650  */
1651 Roo.data.ScriptTagProxy = function(config){
1652     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1653     Roo.apply(this, config);
1654     this.head = document.getElementsByTagName("head")[0];
1655 };
1656
1657 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1658
1659 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1660     /**
1661      * @cfg {String} url The URL from which to request the data object.
1662      */
1663     /**
1664      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1665      */
1666     timeout : 30000,
1667     /**
1668      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1669      * the server the name of the callback function set up by the load call to process the returned data object.
1670      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1671      * javascript output which calls this named function passing the data object as its only parameter.
1672      */
1673     callbackParam : "callback",
1674     /**
1675      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1676      * name to the request.
1677      */
1678     nocache : true,
1679
1680     /**
1681      * Load data from the configured URL, read the data object into
1682      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1683      * process that block using the passed callback.
1684      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1685      * for the request to the remote server.
1686      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1687      * object into a block of Roo.data.Records.
1688      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1689      * The function must be passed <ul>
1690      * <li>The Record block object</li>
1691      * <li>The "arg" argument from the load function</li>
1692      * <li>A boolean success indicator</li>
1693      * </ul>
1694      * @param {Object} scope The scope in which to call the callback
1695      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1696      */
1697     load : function(params, reader, callback, scope, arg){
1698         if(this.fireEvent("beforeload", this, params) !== false){
1699
1700             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1701
1702             var url = this.url;
1703             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1704             if(this.nocache){
1705                 url += "&_dc=" + (new Date().getTime());
1706             }
1707             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1708             var trans = {
1709                 id : transId,
1710                 cb : "stcCallback"+transId,
1711                 scriptId : "stcScript"+transId,
1712                 params : params,
1713                 arg : arg,
1714                 url : url,
1715                 callback : callback,
1716                 scope : scope,
1717                 reader : reader
1718             };
1719             var conn = this;
1720
1721             window[trans.cb] = function(o){
1722                 conn.handleResponse(o, trans);
1723             };
1724
1725             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1726
1727             if(this.autoAbort !== false){
1728                 this.abort();
1729             }
1730
1731             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1732
1733             var script = document.createElement("script");
1734             script.setAttribute("src", url);
1735             script.setAttribute("type", "text/javascript");
1736             script.setAttribute("id", trans.scriptId);
1737             this.head.appendChild(script);
1738
1739             this.trans = trans;
1740         }else{
1741             callback.call(scope||this, null, arg, false);
1742         }
1743     },
1744
1745     // private
1746     isLoading : function(){
1747         return this.trans ? true : false;
1748     },
1749
1750     /**
1751      * Abort the current server request.
1752      */
1753     abort : function(){
1754         if(this.isLoading()){
1755             this.destroyTrans(this.trans);
1756         }
1757     },
1758
1759     // private
1760     destroyTrans : function(trans, isLoaded){
1761         this.head.removeChild(document.getElementById(trans.scriptId));
1762         clearTimeout(trans.timeoutId);
1763         if(isLoaded){
1764             window[trans.cb] = undefined;
1765             try{
1766                 delete window[trans.cb];
1767             }catch(e){}
1768         }else{
1769             // if hasn't been loaded, wait for load to remove it to prevent script error
1770             window[trans.cb] = function(){
1771                 window[trans.cb] = undefined;
1772                 try{
1773                     delete window[trans.cb];
1774                 }catch(e){}
1775             };
1776         }
1777     },
1778
1779     // private
1780     handleResponse : function(o, trans){
1781         this.trans = false;
1782         this.destroyTrans(trans, true);
1783         var result;
1784         try {
1785             result = trans.reader.readRecords(o);
1786         }catch(e){
1787             this.fireEvent("loadexception", this, o, trans.arg, e);
1788             trans.callback.call(trans.scope||window, null, trans.arg, false);
1789             return;
1790         }
1791         this.fireEvent("load", this, o, trans.arg);
1792         trans.callback.call(trans.scope||window, result, trans.arg, true);
1793     },
1794
1795     // private
1796     handleFailure : function(trans){
1797         this.trans = false;
1798         this.destroyTrans(trans, false);
1799         this.fireEvent("loadexception", this, null, trans.arg);
1800         trans.callback.call(trans.scope||window, null, trans.arg, false);
1801     }
1802 });/*
1803  * Based on:
1804  * Ext JS Library 1.1.1
1805  * Copyright(c) 2006-2007, Ext JS, LLC.
1806  *
1807  * Originally Released Under LGPL - original licence link has changed is not relivant.
1808  *
1809  * Fork - LGPL
1810  * <script type="text/javascript">
1811  */
1812
1813 /**
1814  * @class Roo.data.JsonReader
1815  * @extends Roo.data.DataReader
1816  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1817  * based on mappings in a provided Roo.data.Record constructor.
1818  * 
1819  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1820  * in the reply previously. 
1821  * 
1822  * <p>
1823  * Example code:
1824  * <pre><code>
1825 var RecordDef = Roo.data.Record.create([
1826     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1827     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1828 ]);
1829 var myReader = new Roo.data.JsonReader({
1830     totalProperty: "results",    // The property which contains the total dataset size (optional)
1831     root: "rows",                // The property which contains an Array of row objects
1832     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1833 }, RecordDef);
1834 </code></pre>
1835  * <p>
1836  * This would consume a JSON file like this:
1837  * <pre><code>
1838 { 'results': 2, 'rows': [
1839     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1840     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1841 }
1842 </code></pre>
1843  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1844  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1845  * paged from the remote server.
1846  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1847  * @cfg {String} root name of the property which contains the Array of row objects.
1848  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1849  * @cfg {Array} fields Array of field definition objects
1850  * @constructor
1851  * Create a new JsonReader
1852  * @param {Object} meta Metadata configuration options
1853  * @param {Object} recordType Either an Array of field definition objects,
1854  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1855  */
1856 Roo.data.JsonReader = function(meta, recordType){
1857     
1858     meta = meta || {};
1859     // set some defaults:
1860     Roo.applyIf(meta, {
1861         totalProperty: 'total',
1862         successProperty : 'success',
1863         root : 'data',
1864         id : 'id'
1865     });
1866     
1867     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1868 };
1869 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1870     
1871     readerType : 'Json',
1872     
1873     /**
1874      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1875      * Used by Store query builder to append _requestMeta to params.
1876      * 
1877      */
1878     metaFromRemote : false,
1879     /**
1880      * This method is only used by a DataProxy which has retrieved data from a remote server.
1881      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1882      * @return {Object} data A data block which is used by an Roo.data.Store object as
1883      * a cache of Roo.data.Records.
1884      */
1885     read : function(response){
1886         var json = response.responseText;
1887        
1888         var o = /* eval:var:o */ eval("("+json+")");
1889         if(!o) {
1890             throw {message: "JsonReader.read: Json object not found"};
1891         }
1892         
1893         if(o.metaData){
1894             
1895             delete this.ef;
1896             this.metaFromRemote = true;
1897             this.meta = o.metaData;
1898             this.recordType = Roo.data.Record.create(o.metaData.fields);
1899             this.onMetaChange(this.meta, this.recordType, o);
1900         }
1901         return this.readRecords(o);
1902     },
1903
1904     // private function a store will implement
1905     onMetaChange : function(meta, recordType, o){
1906
1907     },
1908
1909     /**
1910          * @ignore
1911          */
1912     simpleAccess: function(obj, subsc) {
1913         return obj[subsc];
1914     },
1915
1916         /**
1917          * @ignore
1918          */
1919     getJsonAccessor: function(){
1920         var re = /[\[\.]/;
1921         return function(expr) {
1922             try {
1923                 return(re.test(expr))
1924                     ? new Function("obj", "return obj." + expr)
1925                     : function(obj){
1926                         return obj[expr];
1927                     };
1928             } catch(e){}
1929             return Roo.emptyFn;
1930         };
1931     }(),
1932
1933     /**
1934      * Create a data block containing Roo.data.Records from an XML document.
1935      * @param {Object} o An object which contains an Array of row objects in the property specified
1936      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1937      * which contains the total size of the dataset.
1938      * @return {Object} data A data block which is used by an Roo.data.Store object as
1939      * a cache of Roo.data.Records.
1940      */
1941     readRecords : function(o){
1942         /**
1943          * After any data loads, the raw JSON data is available for further custom processing.
1944          * @type Object
1945          */
1946         this.o = o;
1947         var s = this.meta, Record = this.recordType,
1948             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1949
1950 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1951         if (!this.ef) {
1952             if(s.totalProperty) {
1953                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1954                 }
1955                 if(s.successProperty) {
1956                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1957                 }
1958                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1959                 if (s.id) {
1960                         var g = this.getJsonAccessor(s.id);
1961                         this.getId = function(rec) {
1962                                 var r = g(rec);  
1963                                 return (r === undefined || r === "") ? null : r;
1964                         };
1965                 } else {
1966                         this.getId = function(){return null;};
1967                 }
1968             this.ef = [];
1969             for(var jj = 0; jj < fl; jj++){
1970                 f = fi[jj];
1971                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1972                 this.ef[jj] = this.getJsonAccessor(map);
1973             }
1974         }
1975
1976         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1977         if(s.totalProperty){
1978             var vt = parseInt(this.getTotal(o), 10);
1979             if(!isNaN(vt)){
1980                 totalRecords = vt;
1981             }
1982         }
1983         if(s.successProperty){
1984             var vs = this.getSuccess(o);
1985             if(vs === false || vs === 'false'){
1986                 success = false;
1987             }
1988         }
1989         var records = [];
1990         for(var i = 0; i < c; i++){
1991             var n = root[i];
1992             var values = {};
1993             var id = this.getId(n);
1994             for(var j = 0; j < fl; j++){
1995                 f = fi[j];
1996                                 var v = this.ef[j](n);
1997                                 if (!f.convert) {
1998                                         Roo.log('missing convert for ' + f.name);
1999                                         Roo.log(f);
2000                                         continue;
2001                                 }
2002                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
2003             }
2004                         if (!Record) {
2005                                 return {
2006                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2007                                         success : false,
2008                                         records : [],
2009                                         totalRecords : 0
2010                                 };
2011                         }
2012             var record = new Record(values, id);
2013             record.json = n;
2014             records[i] = record;
2015         }
2016         return {
2017             raw : o,
2018             success : success,
2019             records : records,
2020             totalRecords : totalRecords
2021         };
2022     },
2023     // used when loading children.. @see loadDataFromChildren
2024     toLoadData: function(rec)
2025     {
2026         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2027         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2028         return { data : data, total : data.length };
2029         
2030     }
2031 });/*
2032  * Based on:
2033  * Ext JS Library 1.1.1
2034  * Copyright(c) 2006-2007, Ext JS, LLC.
2035  *
2036  * Originally Released Under LGPL - original licence link has changed is not relivant.
2037  *
2038  * Fork - LGPL
2039  * <script type="text/javascript">
2040  */
2041
2042 /**
2043  * @class Roo.data.XmlReader
2044  * @extends Roo.data.DataReader
2045  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2046  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2047  * <p>
2048  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2049  * header in the HTTP response must be set to "text/xml".</em>
2050  * <p>
2051  * Example code:
2052  * <pre><code>
2053 var RecordDef = Roo.data.Record.create([
2054    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2055    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2056 ]);
2057 var myReader = new Roo.data.XmlReader({
2058    totalRecords: "results", // The element which contains the total dataset size (optional)
2059    record: "row",           // The repeated element which contains row information
2060    id: "id"                 // The element within the row that provides an ID for the record (optional)
2061 }, RecordDef);
2062 </code></pre>
2063  * <p>
2064  * This would consume an XML file like this:
2065  * <pre><code>
2066 &lt;?xml?>
2067 &lt;dataset>
2068  &lt;results>2&lt;/results>
2069  &lt;row>
2070    &lt;id>1&lt;/id>
2071    &lt;name>Bill&lt;/name>
2072    &lt;occupation>Gardener&lt;/occupation>
2073  &lt;/row>
2074  &lt;row>
2075    &lt;id>2&lt;/id>
2076    &lt;name>Ben&lt;/name>
2077    &lt;occupation>Horticulturalist&lt;/occupation>
2078  &lt;/row>
2079 &lt;/dataset>
2080 </code></pre>
2081  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2082  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2083  * paged from the remote server.
2084  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2085  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2086  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2087  * a record identifier value.
2088  * @constructor
2089  * Create a new XmlReader
2090  * @param {Object} meta Metadata configuration options
2091  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2092  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2093  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2094  */
2095 Roo.data.XmlReader = function(meta, recordType){
2096     meta = meta || {};
2097     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2098 };
2099 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2100     
2101     readerType : 'Xml',
2102     
2103     /**
2104      * This method is only used by a DataProxy which has retrieved data from a remote server.
2105          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2106          * to contain a method called 'responseXML' that returns an XML document object.
2107      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2108      * a cache of Roo.data.Records.
2109      */
2110     read : function(response){
2111         var doc = response.responseXML;
2112         if(!doc) {
2113             throw {message: "XmlReader.read: XML Document not available"};
2114         }
2115         return this.readRecords(doc);
2116     },
2117
2118     /**
2119      * Create a data block containing Roo.data.Records from an XML document.
2120          * @param {Object} doc A parsed XML document.
2121      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2122      * a cache of Roo.data.Records.
2123      */
2124     readRecords : function(doc){
2125         /**
2126          * After any data loads/reads, the raw XML Document is available for further custom processing.
2127          * @type XMLDocument
2128          */
2129         this.xmlData = doc;
2130         var root = doc.documentElement || doc;
2131         var q = Roo.DomQuery;
2132         var recordType = this.recordType, fields = recordType.prototype.fields;
2133         var sid = this.meta.id;
2134         var totalRecords = 0, success = true;
2135         if(this.meta.totalRecords){
2136             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2137         }
2138         
2139         if(this.meta.success){
2140             var sv = q.selectValue(this.meta.success, root, true);
2141             success = sv !== false && sv !== 'false';
2142         }
2143         var records = [];
2144         var ns = q.select(this.meta.record, root);
2145         for(var i = 0, len = ns.length; i < len; i++) {
2146                 var n = ns[i];
2147                 var values = {};
2148                 var id = sid ? q.selectValue(sid, n) : undefined;
2149                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2150                     var f = fields.items[j];
2151                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2152                     v = f.convert(v);
2153                     values[f.name] = v;
2154                 }
2155                 var record = new recordType(values, id);
2156                 record.node = n;
2157                 records[records.length] = record;
2158             }
2159
2160             return {
2161                 success : success,
2162                 records : records,
2163                 totalRecords : totalRecords || records.length
2164             };
2165     }
2166 });/*
2167  * Based on:
2168  * Ext JS Library 1.1.1
2169  * Copyright(c) 2006-2007, Ext JS, LLC.
2170  *
2171  * Originally Released Under LGPL - original licence link has changed is not relivant.
2172  *
2173  * Fork - LGPL
2174  * <script type="text/javascript">
2175  */
2176
2177 /**
2178  * @class Roo.data.ArrayReader
2179  * @extends Roo.data.DataReader
2180  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2181  * Each element of that Array represents a row of data fields. The
2182  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2183  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2184  * <p>
2185  * Example code:.
2186  * <pre><code>
2187 var RecordDef = Roo.data.Record.create([
2188     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2189     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2190 ]);
2191 var myReader = new Roo.data.ArrayReader({
2192     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2193 }, RecordDef);
2194 </code></pre>
2195  * <p>
2196  * This would consume an Array like this:
2197  * <pre><code>
2198 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2199   </code></pre>
2200  
2201  * @constructor
2202  * Create a new JsonReader
2203  * @param {Object} meta Metadata configuration options.
2204  * @param {Object|Array} recordType Either an Array of field definition objects
2205  * 
2206  * @cfg {Array} fields Array of field definition objects
2207  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2208  * as specified to {@link Roo.data.Record#create},
2209  * or an {@link Roo.data.Record} object
2210  *
2211  * 
2212  * created using {@link Roo.data.Record#create}.
2213  */
2214 Roo.data.ArrayReader = function(meta, recordType)
2215 {    
2216     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2217 };
2218
2219 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2220     
2221       /**
2222      * Create a data block containing Roo.data.Records from an XML document.
2223      * @param {Object} o An Array of row objects which represents the dataset.
2224      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2225      * a cache of Roo.data.Records.
2226      */
2227     readRecords : function(o)
2228     {
2229         var sid = this.meta ? this.meta.id : null;
2230         var recordType = this.recordType, fields = recordType.prototype.fields;
2231         var records = [];
2232         var root = o;
2233         for(var i = 0; i < root.length; i++){
2234             var n = root[i];
2235             var values = {};
2236             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2237             for(var j = 0, jlen = fields.length; j < jlen; j++){
2238                 var f = fields.items[j];
2239                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2240                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2241                 v = f.convert(v);
2242                 values[f.name] = v;
2243             }
2244             var record = new recordType(values, id);
2245             record.json = n;
2246             records[records.length] = record;
2247         }
2248         return {
2249             records : records,
2250             totalRecords : records.length
2251         };
2252     },
2253     // used when loading children.. @see loadDataFromChildren
2254     toLoadData: function(rec)
2255     {
2256         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2257         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2258         
2259     }
2260     
2261     
2262 });/*
2263  * Based on:
2264  * Ext JS Library 1.1.1
2265  * Copyright(c) 2006-2007, Ext JS, LLC.
2266  *
2267  * Originally Released Under LGPL - original licence link has changed is not relivant.
2268  *
2269  * Fork - LGPL
2270  * <script type="text/javascript">
2271  */
2272
2273
2274 /**
2275  * @class Roo.data.Tree
2276  * @extends Roo.util.Observable
2277  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2278  * in the tree have most standard DOM functionality.
2279  * @constructor
2280  * @param {Node} root (optional) The root node
2281  */
2282 Roo.data.Tree = function(root){
2283    this.nodeHash = {};
2284    /**
2285     * The root node for this tree
2286     * @type Node
2287     */
2288    this.root = null;
2289    if(root){
2290        this.setRootNode(root);
2291    }
2292    this.addEvents({
2293        /**
2294         * @event append
2295         * Fires when a new child node is appended to a node in this tree.
2296         * @param {Tree} tree The owner tree
2297         * @param {Node} parent The parent node
2298         * @param {Node} node The newly appended node
2299         * @param {Number} index The index of the newly appended node
2300         */
2301        "append" : true,
2302        /**
2303         * @event remove
2304         * Fires when a child node is removed from a node in this tree.
2305         * @param {Tree} tree The owner tree
2306         * @param {Node} parent The parent node
2307         * @param {Node} node The child node removed
2308         */
2309        "remove" : true,
2310        /**
2311         * @event move
2312         * Fires when a node is moved to a new location in the tree
2313         * @param {Tree} tree The owner tree
2314         * @param {Node} node The node moved
2315         * @param {Node} oldParent The old parent of this node
2316         * @param {Node} newParent The new parent of this node
2317         * @param {Number} index The index it was moved to
2318         */
2319        "move" : true,
2320        /**
2321         * @event insert
2322         * Fires when a new child node is inserted in a node in this tree.
2323         * @param {Tree} tree The owner tree
2324         * @param {Node} parent The parent node
2325         * @param {Node} node The child node inserted
2326         * @param {Node} refNode The child node the node was inserted before
2327         */
2328        "insert" : true,
2329        /**
2330         * @event beforeappend
2331         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2332         * @param {Tree} tree The owner tree
2333         * @param {Node} parent The parent node
2334         * @param {Node} node The child node to be appended
2335         */
2336        "beforeappend" : true,
2337        /**
2338         * @event beforeremove
2339         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2340         * @param {Tree} tree The owner tree
2341         * @param {Node} parent The parent node
2342         * @param {Node} node The child node to be removed
2343         */
2344        "beforeremove" : true,
2345        /**
2346         * @event beforemove
2347         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2348         * @param {Tree} tree The owner tree
2349         * @param {Node} node The node being moved
2350         * @param {Node} oldParent The parent of the node
2351         * @param {Node} newParent The new parent the node is moving to
2352         * @param {Number} index The index it is being moved to
2353         */
2354        "beforemove" : true,
2355        /**
2356         * @event beforeinsert
2357         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2358         * @param {Tree} tree The owner tree
2359         * @param {Node} parent The parent node
2360         * @param {Node} node The child node to be inserted
2361         * @param {Node} refNode The child node the node is being inserted before
2362         */
2363        "beforeinsert" : true
2364    });
2365
2366     Roo.data.Tree.superclass.constructor.call(this);
2367 };
2368
2369 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2370     pathSeparator: "/",
2371
2372     proxyNodeEvent : function(){
2373         return this.fireEvent.apply(this, arguments);
2374     },
2375
2376     /**
2377      * Returns the root node for this tree.
2378      * @return {Node}
2379      */
2380     getRootNode : function(){
2381         return this.root;
2382     },
2383
2384     /**
2385      * Sets the root node for this tree.
2386      * @param {Node} node
2387      * @return {Node}
2388      */
2389     setRootNode : function(node){
2390         this.root = node;
2391         node.ownerTree = this;
2392         node.isRoot = true;
2393         this.registerNode(node);
2394         return node;
2395     },
2396
2397     /**
2398      * Gets a node in this tree by its id.
2399      * @param {String} id
2400      * @return {Node}
2401      */
2402     getNodeById : function(id){
2403         return this.nodeHash[id];
2404     },
2405
2406     registerNode : function(node){
2407         this.nodeHash[node.id] = node;
2408     },
2409
2410     unregisterNode : function(node){
2411         delete this.nodeHash[node.id];
2412     },
2413
2414     toString : function(){
2415         return "[Tree"+(this.id?" "+this.id:"")+"]";
2416     }
2417 });
2418
2419 /**
2420  * @class Roo.data.Node
2421  * @extends Roo.util.Observable
2422  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2423  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2424  * @constructor
2425  * @param {Object} attributes The attributes/config for the node
2426  */
2427 Roo.data.Node = function(attributes){
2428     /**
2429      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2430      * @type {Object}
2431      */
2432     this.attributes = attributes || {};
2433     this.leaf = this.attributes.leaf;
2434     /**
2435      * The node id. @type String
2436      */
2437     this.id = this.attributes.id;
2438     if(!this.id){
2439         this.id = Roo.id(null, "ynode-");
2440         this.attributes.id = this.id;
2441     }
2442      
2443     
2444     /**
2445      * All child nodes of this node. @type Array
2446      */
2447     this.childNodes = [];
2448     if(!this.childNodes.indexOf){ // indexOf is a must
2449         this.childNodes.indexOf = function(o){
2450             for(var i = 0, len = this.length; i < len; i++){
2451                 if(this[i] == o) {
2452                     return i;
2453                 }
2454             }
2455             return -1;
2456         };
2457     }
2458     /**
2459      * The parent node for this node. @type Node
2460      */
2461     this.parentNode = null;
2462     /**
2463      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2464      */
2465     this.firstChild = null;
2466     /**
2467      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2468      */
2469     this.lastChild = null;
2470     /**
2471      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2472      */
2473     this.previousSibling = null;
2474     /**
2475      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2476      */
2477     this.nextSibling = null;
2478
2479     this.addEvents({
2480        /**
2481         * @event append
2482         * Fires when a new child node is appended
2483         * @param {Tree} tree The owner tree
2484         * @param {Node} this This node
2485         * @param {Node} node The newly appended node
2486         * @param {Number} index The index of the newly appended node
2487         */
2488        "append" : true,
2489        /**
2490         * @event remove
2491         * Fires when a child node is removed
2492         * @param {Tree} tree The owner tree
2493         * @param {Node} this This node
2494         * @param {Node} node The removed node
2495         */
2496        "remove" : true,
2497        /**
2498         * @event move
2499         * Fires when this node is moved to a new location in the tree
2500         * @param {Tree} tree The owner tree
2501         * @param {Node} this This node
2502         * @param {Node} oldParent The old parent of this node
2503         * @param {Node} newParent The new parent of this node
2504         * @param {Number} index The index it was moved to
2505         */
2506        "move" : true,
2507        /**
2508         * @event insert
2509         * Fires when a new child node is inserted.
2510         * @param {Tree} tree The owner tree
2511         * @param {Node} this This node
2512         * @param {Node} node The child node inserted
2513         * @param {Node} refNode The child node the node was inserted before
2514         */
2515        "insert" : true,
2516        /**
2517         * @event beforeappend
2518         * Fires before a new child is appended, return false to cancel the append.
2519         * @param {Tree} tree The owner tree
2520         * @param {Node} this This node
2521         * @param {Node} node The child node to be appended
2522         */
2523        "beforeappend" : true,
2524        /**
2525         * @event beforeremove
2526         * Fires before a child is removed, return false to cancel the remove.
2527         * @param {Tree} tree The owner tree
2528         * @param {Node} this This node
2529         * @param {Node} node The child node to be removed
2530         */
2531        "beforeremove" : true,
2532        /**
2533         * @event beforemove
2534         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2535         * @param {Tree} tree The owner tree
2536         * @param {Node} this This node
2537         * @param {Node} oldParent The parent of this node
2538         * @param {Node} newParent The new parent this node is moving to
2539         * @param {Number} index The index it is being moved to
2540         */
2541        "beforemove" : true,
2542        /**
2543         * @event beforeinsert
2544         * Fires before a new child is inserted, return false to cancel the insert.
2545         * @param {Tree} tree The owner tree
2546         * @param {Node} this This node
2547         * @param {Node} node The child node to be inserted
2548         * @param {Node} refNode The child node the node is being inserted before
2549         */
2550        "beforeinsert" : true
2551    });
2552     this.listeners = this.attributes.listeners;
2553     Roo.data.Node.superclass.constructor.call(this);
2554 };
2555
2556 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2557     fireEvent : function(evtName){
2558         // first do standard event for this node
2559         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2560             return false;
2561         }
2562         // then bubble it up to the tree if the event wasn't cancelled
2563         var ot = this.getOwnerTree();
2564         if(ot){
2565             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2566                 return false;
2567             }
2568         }
2569         return true;
2570     },
2571
2572     /**
2573      * Returns true if this node is a leaf
2574      * @return {Boolean}
2575      */
2576     isLeaf : function(){
2577         return this.leaf === true;
2578     },
2579
2580     // private
2581     setFirstChild : function(node){
2582         this.firstChild = node;
2583     },
2584
2585     //private
2586     setLastChild : function(node){
2587         this.lastChild = node;
2588     },
2589
2590
2591     /**
2592      * Returns true if this node is the last child of its parent
2593      * @return {Boolean}
2594      */
2595     isLast : function(){
2596        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2597     },
2598
2599     /**
2600      * Returns true if this node is the first child of its parent
2601      * @return {Boolean}
2602      */
2603     isFirst : function(){
2604        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2605     },
2606
2607     hasChildNodes : function(){
2608         return !this.isLeaf() && this.childNodes.length > 0;
2609     },
2610
2611     /**
2612      * Insert node(s) as the last child node of this node.
2613      * @param {Node/Array} node The node or Array of nodes to append
2614      * @return {Node} The appended node if single append, or null if an array was passed
2615      */
2616     appendChild : function(node){
2617         var multi = false;
2618         if(node instanceof Array){
2619             multi = node;
2620         }else if(arguments.length > 1){
2621             multi = arguments;
2622         }
2623         
2624         // if passed an array or multiple args do them one by one
2625         if(multi){
2626             for(var i = 0, len = multi.length; i < len; i++) {
2627                 this.appendChild(multi[i]);
2628             }
2629         }else{
2630             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2631                 return false;
2632             }
2633             var index = this.childNodes.length;
2634             var oldParent = node.parentNode;
2635             // it's a move, make sure we move it cleanly
2636             if(oldParent){
2637                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2638                     return false;
2639                 }
2640                 oldParent.removeChild(node);
2641             }
2642             
2643             index = this.childNodes.length;
2644             if(index == 0){
2645                 this.setFirstChild(node);
2646             }
2647             this.childNodes.push(node);
2648             node.parentNode = this;
2649             var ps = this.childNodes[index-1];
2650             if(ps){
2651                 node.previousSibling = ps;
2652                 ps.nextSibling = node;
2653             }else{
2654                 node.previousSibling = null;
2655             }
2656             node.nextSibling = null;
2657             this.setLastChild(node);
2658             node.setOwnerTree(this.getOwnerTree());
2659             this.fireEvent("append", this.ownerTree, this, node, index);
2660             if(this.ownerTree) {
2661                 this.ownerTree.fireEvent("appendnode", this, node, index);
2662             }
2663             if(oldParent){
2664                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2665             }
2666             return node;
2667         }
2668     },
2669
2670     /**
2671      * Removes a child node from this node.
2672      * @param {Node} node The node to remove
2673      * @return {Node} The removed node
2674      */
2675     removeChild : function(node){
2676         var index = this.childNodes.indexOf(node);
2677         if(index == -1){
2678             return false;
2679         }
2680         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2681             return false;
2682         }
2683
2684         // remove it from childNodes collection
2685         this.childNodes.splice(index, 1);
2686
2687         // update siblings
2688         if(node.previousSibling){
2689             node.previousSibling.nextSibling = node.nextSibling;
2690         }
2691         if(node.nextSibling){
2692             node.nextSibling.previousSibling = node.previousSibling;
2693         }
2694
2695         // update child refs
2696         if(this.firstChild == node){
2697             this.setFirstChild(node.nextSibling);
2698         }
2699         if(this.lastChild == node){
2700             this.setLastChild(node.previousSibling);
2701         }
2702
2703         node.setOwnerTree(null);
2704         // clear any references from the node
2705         node.parentNode = null;
2706         node.previousSibling = null;
2707         node.nextSibling = null;
2708         this.fireEvent("remove", this.ownerTree, this, node);
2709         return node;
2710     },
2711
2712     /**
2713      * Inserts the first node before the second node in this nodes childNodes collection.
2714      * @param {Node} node The node to insert
2715      * @param {Node} refNode The node to insert before (if null the node is appended)
2716      * @return {Node} The inserted node
2717      */
2718     insertBefore : function(node, refNode){
2719         if(!refNode){ // like standard Dom, refNode can be null for append
2720             return this.appendChild(node);
2721         }
2722         // nothing to do
2723         if(node == refNode){
2724             return false;
2725         }
2726
2727         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2728             return false;
2729         }
2730         var index = this.childNodes.indexOf(refNode);
2731         var oldParent = node.parentNode;
2732         var refIndex = index;
2733
2734         // when moving internally, indexes will change after remove
2735         if(oldParent == this && this.childNodes.indexOf(node) < index){
2736             refIndex--;
2737         }
2738
2739         // it's a move, make sure we move it cleanly
2740         if(oldParent){
2741             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2742                 return false;
2743             }
2744             oldParent.removeChild(node);
2745         }
2746         if(refIndex == 0){
2747             this.setFirstChild(node);
2748         }
2749         this.childNodes.splice(refIndex, 0, node);
2750         node.parentNode = this;
2751         var ps = this.childNodes[refIndex-1];
2752         if(ps){
2753             node.previousSibling = ps;
2754             ps.nextSibling = node;
2755         }else{
2756             node.previousSibling = null;
2757         }
2758         node.nextSibling = refNode;
2759         refNode.previousSibling = node;
2760         node.setOwnerTree(this.getOwnerTree());
2761         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2762         if(oldParent){
2763             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2764         }
2765         return node;
2766     },
2767
2768     /**
2769      * Returns the child node at the specified index.
2770      * @param {Number} index
2771      * @return {Node}
2772      */
2773     item : function(index){
2774         return this.childNodes[index];
2775     },
2776
2777     /**
2778      * Replaces one child node in this node with another.
2779      * @param {Node} newChild The replacement node
2780      * @param {Node} oldChild The node to replace
2781      * @return {Node} The replaced node
2782      */
2783     replaceChild : function(newChild, oldChild){
2784         this.insertBefore(newChild, oldChild);
2785         this.removeChild(oldChild);
2786         return oldChild;
2787     },
2788
2789     /**
2790      * Returns the index of a child node
2791      * @param {Node} node
2792      * @return {Number} The index of the node or -1 if it was not found
2793      */
2794     indexOf : function(child){
2795         return this.childNodes.indexOf(child);
2796     },
2797
2798     /**
2799      * Returns the tree this node is in.
2800      * @return {Tree}
2801      */
2802     getOwnerTree : function(){
2803         // if it doesn't have one, look for one
2804         if(!this.ownerTree){
2805             var p = this;
2806             while(p){
2807                 if(p.ownerTree){
2808                     this.ownerTree = p.ownerTree;
2809                     break;
2810                 }
2811                 p = p.parentNode;
2812             }
2813         }
2814         return this.ownerTree;
2815     },
2816
2817     /**
2818      * Returns depth of this node (the root node has a depth of 0)
2819      * @return {Number}
2820      */
2821     getDepth : function(){
2822         var depth = 0;
2823         var p = this;
2824         while(p.parentNode){
2825             ++depth;
2826             p = p.parentNode;
2827         }
2828         return depth;
2829     },
2830
2831     // private
2832     setOwnerTree : function(tree){
2833         // if it's move, we need to update everyone
2834         if(tree != this.ownerTree){
2835             if(this.ownerTree){
2836                 this.ownerTree.unregisterNode(this);
2837             }
2838             this.ownerTree = tree;
2839             var cs = this.childNodes;
2840             for(var i = 0, len = cs.length; i < len; i++) {
2841                 cs[i].setOwnerTree(tree);
2842             }
2843             if(tree){
2844                 tree.registerNode(this);
2845             }
2846         }
2847     },
2848
2849     /**
2850      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2851      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2852      * @return {String} The path
2853      */
2854     getPath : function(attr){
2855         attr = attr || "id";
2856         var p = this.parentNode;
2857         var b = [this.attributes[attr]];
2858         while(p){
2859             b.unshift(p.attributes[attr]);
2860             p = p.parentNode;
2861         }
2862         var sep = this.getOwnerTree().pathSeparator;
2863         return sep + b.join(sep);
2864     },
2865
2866     /**
2867      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2868      * function call will be the scope provided or the current node. The arguments to the function
2869      * will be the args provided or the current node. If the function returns false at any point,
2870      * the bubble is stopped.
2871      * @param {Function} fn The function to call
2872      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2873      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2874      */
2875     bubble : function(fn, scope, args){
2876         var p = this;
2877         while(p){
2878             if(fn.call(scope || p, args || p) === false){
2879                 break;
2880             }
2881             p = p.parentNode;
2882         }
2883     },
2884
2885     /**
2886      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2887      * function call will be the scope provided or the current node. The arguments to the function
2888      * will be the args provided or the current node. If the function returns false at any point,
2889      * the cascade is stopped on that branch.
2890      * @param {Function} fn The function to call
2891      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2892      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2893      */
2894     cascade : function(fn, scope, args){
2895         if(fn.call(scope || this, args || this) !== false){
2896             var cs = this.childNodes;
2897             for(var i = 0, len = cs.length; i < len; i++) {
2898                 cs[i].cascade(fn, scope, args);
2899             }
2900         }
2901     },
2902
2903     /**
2904      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2905      * function call will be the scope provided or the current node. The arguments to the function
2906      * will be the args provided or the current node. If the function returns false at any point,
2907      * the iteration stops.
2908      * @param {Function} fn The function to call
2909      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2910      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2911      */
2912     eachChild : function(fn, scope, args){
2913         var cs = this.childNodes;
2914         for(var i = 0, len = cs.length; i < len; i++) {
2915                 if(fn.call(scope || this, args || cs[i]) === false){
2916                     break;
2917                 }
2918         }
2919     },
2920
2921     /**
2922      * Finds the first child that has the attribute with the specified value.
2923      * @param {String} attribute The attribute name
2924      * @param {Mixed} value The value to search for
2925      * @return {Node} The found child or null if none was found
2926      */
2927     findChild : function(attribute, value){
2928         var cs = this.childNodes;
2929         for(var i = 0, len = cs.length; i < len; i++) {
2930                 if(cs[i].attributes[attribute] == value){
2931                     return cs[i];
2932                 }
2933         }
2934         return null;
2935     },
2936
2937     /**
2938      * Finds the first child by a custom function. The child matches if the function passed
2939      * returns true.
2940      * @param {Function} fn
2941      * @param {Object} scope (optional)
2942      * @return {Node} The found child or null if none was found
2943      */
2944     findChildBy : function(fn, scope){
2945         var cs = this.childNodes;
2946         for(var i = 0, len = cs.length; i < len; i++) {
2947                 if(fn.call(scope||cs[i], cs[i]) === true){
2948                     return cs[i];
2949                 }
2950         }
2951         return null;
2952     },
2953
2954     /**
2955      * Sorts this nodes children using the supplied sort function
2956      * @param {Function} fn
2957      * @param {Object} scope (optional)
2958      */
2959     sort : function(fn, scope){
2960         var cs = this.childNodes;
2961         var len = cs.length;
2962         if(len > 0){
2963             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2964             cs.sort(sortFn);
2965             for(var i = 0; i < len; i++){
2966                 var n = cs[i];
2967                 n.previousSibling = cs[i-1];
2968                 n.nextSibling = cs[i+1];
2969                 if(i == 0){
2970                     this.setFirstChild(n);
2971                 }
2972                 if(i == len-1){
2973                     this.setLastChild(n);
2974                 }
2975             }
2976         }
2977     },
2978
2979     /**
2980      * Returns true if this node is an ancestor (at any point) of the passed node.
2981      * @param {Node} node
2982      * @return {Boolean}
2983      */
2984     contains : function(node){
2985         return node.isAncestor(this);
2986     },
2987
2988     /**
2989      * Returns true if the passed node is an ancestor (at any point) of this node.
2990      * @param {Node} node
2991      * @return {Boolean}
2992      */
2993     isAncestor : function(node){
2994         var p = this.parentNode;
2995         while(p){
2996             if(p == node){
2997                 return true;
2998             }
2999             p = p.parentNode;
3000         }
3001         return false;
3002     },
3003
3004     toString : function(){
3005         return "[Node"+(this.id?" "+this.id:"")+"]";
3006     }
3007 });/*
3008  * Based on:
3009  * Ext JS Library 1.1.1
3010  * Copyright(c) 2006-2007, Ext JS, LLC.
3011  *
3012  * Originally Released Under LGPL - original licence link has changed is not relivant.
3013  *
3014  * Fork - LGPL
3015  * <script type="text/javascript">
3016  */
3017
3018
3019 /**
3020  * @class Roo.Shadow
3021  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3022  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3023  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3024  * @constructor
3025  * Create a new Shadow
3026  * @param {Object} config The config object
3027  */
3028 Roo.Shadow = function(config){
3029     Roo.apply(this, config);
3030     if(typeof this.mode != "string"){
3031         this.mode = this.defaultMode;
3032     }
3033     var o = this.offset, a = {h: 0};
3034     var rad = Math.floor(this.offset/2);
3035     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3036         case "drop":
3037             a.w = 0;
3038             a.l = a.t = o;
3039             a.t -= 1;
3040             if(Roo.isIE){
3041                 a.l -= this.offset + rad;
3042                 a.t -= this.offset + rad;
3043                 a.w -= rad;
3044                 a.h -= rad;
3045                 a.t += 1;
3046             }
3047         break;
3048         case "sides":
3049             a.w = (o*2);
3050             a.l = -o;
3051             a.t = o-1;
3052             if(Roo.isIE){
3053                 a.l -= (this.offset - rad);
3054                 a.t -= this.offset + rad;
3055                 a.l += 1;
3056                 a.w -= (this.offset - rad)*2;
3057                 a.w -= rad + 1;
3058                 a.h -= 1;
3059             }
3060         break;
3061         case "frame":
3062             a.w = a.h = (o*2);
3063             a.l = a.t = -o;
3064             a.t += 1;
3065             a.h -= 2;
3066             if(Roo.isIE){
3067                 a.l -= (this.offset - rad);
3068                 a.t -= (this.offset - rad);
3069                 a.l += 1;
3070                 a.w -= (this.offset + rad + 1);
3071                 a.h -= (this.offset + rad);
3072                 a.h += 1;
3073             }
3074         break;
3075     };
3076
3077     this.adjusts = a;
3078 };
3079
3080 Roo.Shadow.prototype = {
3081     /**
3082      * @cfg {String} mode
3083      * The shadow display mode.  Supports the following options:<br />
3084      * sides: Shadow displays on both sides and bottom only<br />
3085      * frame: Shadow displays equally on all four sides<br />
3086      * drop: Traditional bottom-right drop shadow (default)
3087      */
3088     mode: false,
3089     /**
3090      * @cfg {String} offset
3091      * The number of pixels to offset the shadow from the element (defaults to 4)
3092      */
3093     offset: 4,
3094
3095     // private
3096     defaultMode: "drop",
3097
3098     /**
3099      * Displays the shadow under the target element
3100      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3101      */
3102     show : function(target){
3103         target = Roo.get(target);
3104         if(!this.el){
3105             this.el = Roo.Shadow.Pool.pull();
3106             if(this.el.dom.nextSibling != target.dom){
3107                 this.el.insertBefore(target);
3108             }
3109         }
3110         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3111         if(Roo.isIE){
3112             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3113         }
3114         this.realign(
3115             target.getLeft(true),
3116             target.getTop(true),
3117             target.getWidth(),
3118             target.getHeight()
3119         );
3120         this.el.dom.style.display = "block";
3121     },
3122
3123     /**
3124      * Returns true if the shadow is visible, else false
3125      */
3126     isVisible : function(){
3127         return this.el ? true : false;  
3128     },
3129
3130     /**
3131      * Direct alignment when values are already available. Show must be called at least once before
3132      * calling this method to ensure it is initialized.
3133      * @param {Number} left The target element left position
3134      * @param {Number} top The target element top position
3135      * @param {Number} width The target element width
3136      * @param {Number} height The target element height
3137      */
3138     realign : function(l, t, w, h){
3139         if(!this.el){
3140             return;
3141         }
3142         var a = this.adjusts, d = this.el.dom, s = d.style;
3143         var iea = 0;
3144         s.left = (l+a.l)+"px";
3145         s.top = (t+a.t)+"px";
3146         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3147  
3148         if(s.width != sws || s.height != shs){
3149             s.width = sws;
3150             s.height = shs;
3151             if(!Roo.isIE){
3152                 var cn = d.childNodes;
3153                 var sww = Math.max(0, (sw-12))+"px";
3154                 cn[0].childNodes[1].style.width = sww;
3155                 cn[1].childNodes[1].style.width = sww;
3156                 cn[2].childNodes[1].style.width = sww;
3157                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3158             }
3159         }
3160     },
3161
3162     /**
3163      * Hides this shadow
3164      */
3165     hide : function(){
3166         if(this.el){
3167             this.el.dom.style.display = "none";
3168             Roo.Shadow.Pool.push(this.el);
3169             delete this.el;
3170         }
3171     },
3172
3173     /**
3174      * Adjust the z-index of this shadow
3175      * @param {Number} zindex The new z-index
3176      */
3177     setZIndex : function(z){
3178         this.zIndex = z;
3179         if(this.el){
3180             this.el.setStyle("z-index", z);
3181         }
3182     }
3183 };
3184
3185 // Private utility class that manages the internal Shadow cache
3186 Roo.Shadow.Pool = function(){
3187     var p = [];
3188     var markup = Roo.isIE ?
3189                  '<div class="x-ie-shadow"></div>' :
3190                  '<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>';
3191     return {
3192         pull : function(){
3193             var sh = p.shift();
3194             if(!sh){
3195                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3196                 sh.autoBoxAdjust = false;
3197             }
3198             return sh;
3199         },
3200
3201         push : function(sh){
3202             p.push(sh);
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215
3216
3217 /**
3218  * @class Roo.SplitBar
3219  * @extends Roo.util.Observable
3220  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3221  * <br><br>
3222  * Usage:
3223  * <pre><code>
3224 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3225                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3226 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3227 split.minSize = 100;
3228 split.maxSize = 600;
3229 split.animate = true;
3230 split.on('moved', splitterMoved);
3231 </code></pre>
3232  * @constructor
3233  * Create a new SplitBar
3234  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3235  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3236  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3237  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3238                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3239                         position of the SplitBar).
3240  */
3241 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3242     
3243     /** @private */
3244     this.el = Roo.get(dragElement, true);
3245     this.el.dom.unselectable = "on";
3246     /** @private */
3247     this.resizingEl = Roo.get(resizingElement, true);
3248
3249     /**
3250      * @private
3251      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3252      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3253      * @type Number
3254      */
3255     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3256     
3257     /**
3258      * The minimum size of the resizing element. (Defaults to 0)
3259      * @type Number
3260      */
3261     this.minSize = 0;
3262     
3263     /**
3264      * The maximum size of the resizing element. (Defaults to 2000)
3265      * @type Number
3266      */
3267     this.maxSize = 2000;
3268     
3269     /**
3270      * Whether to animate the transition to the new size
3271      * @type Boolean
3272      */
3273     this.animate = false;
3274     
3275     /**
3276      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3277      * @type Boolean
3278      */
3279     this.useShim = false;
3280     
3281     /** @private */
3282     this.shim = null;
3283     
3284     if(!existingProxy){
3285         /** @private */
3286         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3287     }else{
3288         this.proxy = Roo.get(existingProxy).dom;
3289     }
3290     /** @private */
3291     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3292     
3293     /** @private */
3294     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3295     
3296     /** @private */
3297     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3298     
3299     /** @private */
3300     this.dragSpecs = {};
3301     
3302     /**
3303      * @private The adapter to use to positon and resize elements
3304      */
3305     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3306     this.adapter.init(this);
3307     
3308     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3309         /** @private */
3310         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3311         this.el.addClass("x-splitbar-h");
3312     }else{
3313         /** @private */
3314         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3315         this.el.addClass("x-splitbar-v");
3316     }
3317     
3318     this.addEvents({
3319         /**
3320          * @event resize
3321          * Fires when the splitter is moved (alias for {@link #event-moved})
3322          * @param {Roo.SplitBar} this
3323          * @param {Number} newSize the new width or height
3324          */
3325         "resize" : true,
3326         /**
3327          * @event moved
3328          * Fires when the splitter is moved
3329          * @param {Roo.SplitBar} this
3330          * @param {Number} newSize the new width or height
3331          */
3332         "moved" : true,
3333         /**
3334          * @event beforeresize
3335          * Fires before the splitter is dragged
3336          * @param {Roo.SplitBar} this
3337          */
3338         "beforeresize" : true,
3339
3340         "beforeapply" : true
3341     });
3342
3343     Roo.util.Observable.call(this);
3344 };
3345
3346 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3347     onStartProxyDrag : function(x, y){
3348         this.fireEvent("beforeresize", this);
3349         if(!this.overlay){
3350             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3351             o.unselectable();
3352             o.enableDisplayMode("block");
3353             // all splitbars share the same overlay
3354             Roo.SplitBar.prototype.overlay = o;
3355         }
3356         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3357         this.overlay.show();
3358         Roo.get(this.proxy).setDisplayed("block");
3359         var size = this.adapter.getElementSize(this);
3360         this.activeMinSize = this.getMinimumSize();;
3361         this.activeMaxSize = this.getMaximumSize();;
3362         var c1 = size - this.activeMinSize;
3363         var c2 = Math.max(this.activeMaxSize - size, 0);
3364         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3365             this.dd.resetConstraints();
3366             this.dd.setXConstraint(
3367                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3368                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3369             );
3370             this.dd.setYConstraint(0, 0);
3371         }else{
3372             this.dd.resetConstraints();
3373             this.dd.setXConstraint(0, 0);
3374             this.dd.setYConstraint(
3375                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3376                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3377             );
3378          }
3379         this.dragSpecs.startSize = size;
3380         this.dragSpecs.startPoint = [x, y];
3381         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3382     },
3383     
3384     /** 
3385      * @private Called after the drag operation by the DDProxy
3386      */
3387     onEndProxyDrag : function(e){
3388         Roo.get(this.proxy).setDisplayed(false);
3389         var endPoint = Roo.lib.Event.getXY(e);
3390         if(this.overlay){
3391             this.overlay.hide();
3392         }
3393         var newSize;
3394         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3395             newSize = this.dragSpecs.startSize + 
3396                 (this.placement == Roo.SplitBar.LEFT ?
3397                     endPoint[0] - this.dragSpecs.startPoint[0] :
3398                     this.dragSpecs.startPoint[0] - endPoint[0]
3399                 );
3400         }else{
3401             newSize = this.dragSpecs.startSize + 
3402                 (this.placement == Roo.SplitBar.TOP ?
3403                     endPoint[1] - this.dragSpecs.startPoint[1] :
3404                     this.dragSpecs.startPoint[1] - endPoint[1]
3405                 );
3406         }
3407         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3408         if(newSize != this.dragSpecs.startSize){
3409             if(this.fireEvent('beforeapply', this, newSize) !== false){
3410                 this.adapter.setElementSize(this, newSize);
3411                 this.fireEvent("moved", this, newSize);
3412                 this.fireEvent("resize", this, newSize);
3413             }
3414         }
3415     },
3416     
3417     /**
3418      * Get the adapter this SplitBar uses
3419      * @return The adapter object
3420      */
3421     getAdapter : function(){
3422         return this.adapter;
3423     },
3424     
3425     /**
3426      * Set the adapter this SplitBar uses
3427      * @param {Object} adapter A SplitBar adapter object
3428      */
3429     setAdapter : function(adapter){
3430         this.adapter = adapter;
3431         this.adapter.init(this);
3432     },
3433     
3434     /**
3435      * Gets the minimum size for the resizing element
3436      * @return {Number} The minimum size
3437      */
3438     getMinimumSize : function(){
3439         return this.minSize;
3440     },
3441     
3442     /**
3443      * Sets the minimum size for the resizing element
3444      * @param {Number} minSize The minimum size
3445      */
3446     setMinimumSize : function(minSize){
3447         this.minSize = minSize;
3448     },
3449     
3450     /**
3451      * Gets the maximum size for the resizing element
3452      * @return {Number} The maximum size
3453      */
3454     getMaximumSize : function(){
3455         return this.maxSize;
3456     },
3457     
3458     /**
3459      * Sets the maximum size for the resizing element
3460      * @param {Number} maxSize The maximum size
3461      */
3462     setMaximumSize : function(maxSize){
3463         this.maxSize = maxSize;
3464     },
3465     
3466     /**
3467      * Sets the initialize size for the resizing element
3468      * @param {Number} size The initial size
3469      */
3470     setCurrentSize : function(size){
3471         var oldAnimate = this.animate;
3472         this.animate = false;
3473         this.adapter.setElementSize(this, size);
3474         this.animate = oldAnimate;
3475     },
3476     
3477     /**
3478      * Destroy this splitbar. 
3479      * @param {Boolean} removeEl True to remove the element
3480      */
3481     destroy : function(removeEl){
3482         if(this.shim){
3483             this.shim.remove();
3484         }
3485         this.dd.unreg();
3486         this.proxy.parentNode.removeChild(this.proxy);
3487         if(removeEl){
3488             this.el.remove();
3489         }
3490     }
3491 });
3492
3493 /**
3494  * @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.
3495  */
3496 Roo.SplitBar.createProxy = function(dir){
3497     var proxy = new Roo.Element(document.createElement("div"));
3498     proxy.unselectable();
3499     var cls = 'x-splitbar-proxy';
3500     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3501     document.body.appendChild(proxy.dom);
3502     return proxy.dom;
3503 };
3504
3505 /** 
3506  * @class Roo.SplitBar.BasicLayoutAdapter
3507  * Default Adapter. It assumes the splitter and resizing element are not positioned
3508  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3509  */
3510 Roo.SplitBar.BasicLayoutAdapter = function(){
3511 };
3512
3513 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3514     // do nothing for now
3515     init : function(s){
3516     
3517     },
3518     /**
3519      * Called before drag operations to get the current size of the resizing element. 
3520      * @param {Roo.SplitBar} s The SplitBar using this adapter
3521      */
3522      getElementSize : function(s){
3523         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3524             return s.resizingEl.getWidth();
3525         }else{
3526             return s.resizingEl.getHeight();
3527         }
3528     },
3529     
3530     /**
3531      * Called after drag operations to set the size of the resizing element.
3532      * @param {Roo.SplitBar} s The SplitBar using this adapter
3533      * @param {Number} newSize The new size to set
3534      * @param {Function} onComplete A function to be invoked when resizing is complete
3535      */
3536     setElementSize : function(s, newSize, onComplete){
3537         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3538             if(!s.animate){
3539                 s.resizingEl.setWidth(newSize);
3540                 if(onComplete){
3541                     onComplete(s, newSize);
3542                 }
3543             }else{
3544                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3545             }
3546         }else{
3547             
3548             if(!s.animate){
3549                 s.resizingEl.setHeight(newSize);
3550                 if(onComplete){
3551                     onComplete(s, newSize);
3552                 }
3553             }else{
3554                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3555             }
3556         }
3557     }
3558 };
3559
3560 /** 
3561  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3562  * @extends Roo.SplitBar.BasicLayoutAdapter
3563  * Adapter that  moves the splitter element to align with the resized sizing element. 
3564  * Used with an absolute positioned SplitBar.
3565  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3566  * document.body, make sure you assign an id to the body element.
3567  */
3568 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3569     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3570     this.container = Roo.get(container);
3571 };
3572
3573 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3574     init : function(s){
3575         this.basic.init(s);
3576     },
3577     
3578     getElementSize : function(s){
3579         return this.basic.getElementSize(s);
3580     },
3581     
3582     setElementSize : function(s, newSize, onComplete){
3583         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3584     },
3585     
3586     moveSplitter : function(s){
3587         var yes = Roo.SplitBar;
3588         switch(s.placement){
3589             case yes.LEFT:
3590                 s.el.setX(s.resizingEl.getRight());
3591                 break;
3592             case yes.RIGHT:
3593                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3594                 break;
3595             case yes.TOP:
3596                 s.el.setY(s.resizingEl.getBottom());
3597                 break;
3598             case yes.BOTTOM:
3599                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3600                 break;
3601         }
3602     }
3603 };
3604
3605 /**
3606  * Orientation constant - Create a vertical SplitBar
3607  * @static
3608  * @type Number
3609  */
3610 Roo.SplitBar.VERTICAL = 1;
3611
3612 /**
3613  * Orientation constant - Create a horizontal SplitBar
3614  * @static
3615  * @type Number
3616  */
3617 Roo.SplitBar.HORIZONTAL = 2;
3618
3619 /**
3620  * Placement constant - The resizing element is to the left of the splitter element
3621  * @static
3622  * @type Number
3623  */
3624 Roo.SplitBar.LEFT = 1;
3625
3626 /**
3627  * Placement constant - The resizing element is to the right of the splitter element
3628  * @static
3629  * @type Number
3630  */
3631 Roo.SplitBar.RIGHT = 2;
3632
3633 /**
3634  * Placement constant - The resizing element is positioned above the splitter element
3635  * @static
3636  * @type Number
3637  */
3638 Roo.SplitBar.TOP = 3;
3639
3640 /**
3641  * Placement constant - The resizing element is positioned under splitter element
3642  * @static
3643  * @type Number
3644  */
3645 Roo.SplitBar.BOTTOM = 4;
3646 /*
3647  * Based on:
3648  * Ext JS Library 1.1.1
3649  * Copyright(c) 2006-2007, Ext JS, LLC.
3650  *
3651  * Originally Released Under LGPL - original licence link has changed is not relivant.
3652  *
3653  * Fork - LGPL
3654  * <script type="text/javascript">
3655  */
3656
3657 /**
3658  * @class Roo.View
3659  * @extends Roo.util.Observable
3660  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3661  * This class also supports single and multi selection modes. <br>
3662  * Create a data model bound view:
3663  <pre><code>
3664  var store = new Roo.data.Store(...);
3665
3666  var view = new Roo.View({
3667     el : "my-element",
3668     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3669  
3670     singleSelect: true,
3671     selectedClass: "ydataview-selected",
3672     store: store
3673  });
3674
3675  // listen for node click?
3676  view.on("click", function(vw, index, node, e){
3677  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3678  });
3679
3680  // load XML data
3681  dataModel.load("foobar.xml");
3682  </code></pre>
3683  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3684  * <br><br>
3685  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3686  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3687  * 
3688  * Note: old style constructor is still suported (container, template, config)
3689  * 
3690  * @constructor
3691  * Create a new View
3692  * @param {Object} config The config object
3693  * 
3694  */
3695 Roo.View = function(config, depreciated_tpl, depreciated_config){
3696     
3697     this.parent = false;
3698     
3699     if (typeof(depreciated_tpl) == 'undefined') {
3700         // new way.. - universal constructor.
3701         Roo.apply(this, config);
3702         this.el  = Roo.get(this.el);
3703     } else {
3704         // old format..
3705         this.el  = Roo.get(config);
3706         this.tpl = depreciated_tpl;
3707         Roo.apply(this, depreciated_config);
3708     }
3709     this.wrapEl  = this.el.wrap().wrap();
3710     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3711     
3712     
3713     if(typeof(this.tpl) == "string"){
3714         this.tpl = new Roo.Template(this.tpl);
3715     } else {
3716         // support xtype ctors..
3717         this.tpl = new Roo.factory(this.tpl, Roo);
3718     }
3719     
3720     
3721     this.tpl.compile();
3722     
3723     /** @private */
3724     this.addEvents({
3725         /**
3726          * @event beforeclick
3727          * Fires before a click is processed. Returns false to cancel the default action.
3728          * @param {Roo.View} this
3729          * @param {Number} index The index of the target node
3730          * @param {HTMLElement} node The target node
3731          * @param {Roo.EventObject} e The raw event object
3732          */
3733             "beforeclick" : true,
3734         /**
3735          * @event click
3736          * Fires when a template node is clicked.
3737          * @param {Roo.View} this
3738          * @param {Number} index The index of the target node
3739          * @param {HTMLElement} node The target node
3740          * @param {Roo.EventObject} e The raw event object
3741          */
3742             "click" : true,
3743         /**
3744          * @event dblclick
3745          * Fires when a template node is double clicked.
3746          * @param {Roo.View} this
3747          * @param {Number} index The index of the target node
3748          * @param {HTMLElement} node The target node
3749          * @param {Roo.EventObject} e The raw event object
3750          */
3751             "dblclick" : true,
3752         /**
3753          * @event contextmenu
3754          * Fires when a template node is right clicked.
3755          * @param {Roo.View} this
3756          * @param {Number} index The index of the target node
3757          * @param {HTMLElement} node The target node
3758          * @param {Roo.EventObject} e The raw event object
3759          */
3760             "contextmenu" : true,
3761         /**
3762          * @event selectionchange
3763          * Fires when the selected nodes change.
3764          * @param {Roo.View} this
3765          * @param {Array} selections Array of the selected nodes
3766          */
3767             "selectionchange" : true,
3768     
3769         /**
3770          * @event beforeselect
3771          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3772          * @param {Roo.View} this
3773          * @param {HTMLElement} node The node to be selected
3774          * @param {Array} selections Array of currently selected nodes
3775          */
3776             "beforeselect" : true,
3777         /**
3778          * @event preparedata
3779          * Fires on every row to render, to allow you to change the data.
3780          * @param {Roo.View} this
3781          * @param {Object} data to be rendered (change this)
3782          */
3783           "preparedata" : true
3784           
3785           
3786         });
3787
3788
3789
3790     this.el.on({
3791         "click": this.onClick,
3792         "dblclick": this.onDblClick,
3793         "contextmenu": this.onContextMenu,
3794         scope:this
3795     });
3796
3797     this.selections = [];
3798     this.nodes = [];
3799     this.cmp = new Roo.CompositeElementLite([]);
3800     if(this.store){
3801         this.store = Roo.factory(this.store, Roo.data);
3802         this.setStore(this.store, true);
3803     }
3804     
3805     if ( this.footer && this.footer.xtype) {
3806            
3807          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3808         
3809         this.footer.dataSource = this.store;
3810         this.footer.container = fctr;
3811         this.footer = Roo.factory(this.footer, Roo);
3812         fctr.insertFirst(this.el);
3813         
3814         // this is a bit insane - as the paging toolbar seems to detach the el..
3815 //        dom.parentNode.parentNode.parentNode
3816          // they get detached?
3817     }
3818     
3819     
3820     Roo.View.superclass.constructor.call(this);
3821     
3822     
3823 };
3824
3825 Roo.extend(Roo.View, Roo.util.Observable, {
3826     
3827      /**
3828      * @cfg {Roo.data.Store} store Data store to load data from.
3829      */
3830     store : false,
3831     
3832     /**
3833      * @cfg {String|Roo.Element} el The container element.
3834      */
3835     el : '',
3836     
3837     /**
3838      * @cfg {String|Roo.Template} tpl The template used by this View 
3839      */
3840     tpl : false,
3841     /**
3842      * @cfg {String} dataName the named area of the template to use as the data area
3843      *                          Works with domtemplates roo-name="name"
3844      */
3845     dataName: false,
3846     /**
3847      * @cfg {String} selectedClass The css class to add to selected nodes
3848      */
3849     selectedClass : "x-view-selected",
3850      /**
3851      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3852      */
3853     emptyText : "",
3854     
3855     /**
3856      * @cfg {String} text to display on mask (default Loading)
3857      */
3858     mask : false,
3859     /**
3860      * @cfg {Boolean} multiSelect Allow multiple selection
3861      */
3862     multiSelect : false,
3863     /**
3864      * @cfg {Boolean} singleSelect Allow single selection
3865      */
3866     singleSelect:  false,
3867     
3868     /**
3869      * @cfg {Boolean} toggleSelect - selecting 
3870      */
3871     toggleSelect : false,
3872     
3873     /**
3874      * @cfg {Boolean} tickable - selecting 
3875      */
3876     tickable : false,
3877     
3878     /**
3879      * Returns the element this view is bound to.
3880      * @return {Roo.Element}
3881      */
3882     getEl : function(){
3883         return this.wrapEl;
3884     },
3885     
3886     
3887
3888     /**
3889      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3890      */
3891     refresh : function(){
3892         //Roo.log('refresh');
3893         var t = this.tpl;
3894         
3895         // if we are using something like 'domtemplate', then
3896         // the what gets used is:
3897         // t.applySubtemplate(NAME, data, wrapping data..)
3898         // the outer template then get' applied with
3899         //     the store 'extra data'
3900         // and the body get's added to the
3901         //      roo-name="data" node?
3902         //      <span class='roo-tpl-{name}'></span> ?????
3903         
3904         
3905         
3906         this.clearSelections();
3907         this.el.update("");
3908         var html = [];
3909         var records = this.store.getRange();
3910         if(records.length < 1) {
3911             
3912             // is this valid??  = should it render a template??
3913             
3914             this.el.update(this.emptyText);
3915             return;
3916         }
3917         var el = this.el;
3918         if (this.dataName) {
3919             this.el.update(t.apply(this.store.meta)); //????
3920             el = this.el.child('.roo-tpl-' + this.dataName);
3921         }
3922         
3923         for(var i = 0, len = records.length; i < len; i++){
3924             var data = this.prepareData(records[i].data, i, records[i]);
3925             this.fireEvent("preparedata", this, data, i, records[i]);
3926             
3927             var d = Roo.apply({}, data);
3928             
3929             if(this.tickable){
3930                 Roo.apply(d, {'roo-id' : Roo.id()});
3931                 
3932                 var _this = this;
3933             
3934                 Roo.each(this.parent.item, function(item){
3935                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3936                         return;
3937                     }
3938                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3939                 });
3940             }
3941             
3942             html[html.length] = Roo.util.Format.trim(
3943                 this.dataName ?
3944                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3945                     t.apply(d)
3946             );
3947         }
3948         
3949         
3950         
3951         el.update(html.join(""));
3952         this.nodes = el.dom.childNodes;
3953         this.updateIndexes(0);
3954     },
3955     
3956
3957     /**
3958      * Function to override to reformat the data that is sent to
3959      * the template for each node.
3960      * DEPRICATED - use the preparedata event handler.
3961      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3962      * a JSON object for an UpdateManager bound view).
3963      */
3964     prepareData : function(data, index, record)
3965     {
3966         this.fireEvent("preparedata", this, data, index, record);
3967         return data;
3968     },
3969
3970     onUpdate : function(ds, record){
3971         // Roo.log('on update');   
3972         this.clearSelections();
3973         var index = this.store.indexOf(record);
3974         var n = this.nodes[index];
3975         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3976         n.parentNode.removeChild(n);
3977         this.updateIndexes(index, index);
3978     },
3979
3980     
3981     
3982 // --------- FIXME     
3983     onAdd : function(ds, records, index)
3984     {
3985         //Roo.log(['on Add', ds, records, index] );        
3986         this.clearSelections();
3987         if(this.nodes.length == 0){
3988             this.refresh();
3989             return;
3990         }
3991         var n = this.nodes[index];
3992         for(var i = 0, len = records.length; i < len; i++){
3993             var d = this.prepareData(records[i].data, i, records[i]);
3994             if(n){
3995                 this.tpl.insertBefore(n, d);
3996             }else{
3997                 
3998                 this.tpl.append(this.el, d);
3999             }
4000         }
4001         this.updateIndexes(index);
4002     },
4003
4004     onRemove : function(ds, record, index){
4005        // Roo.log('onRemove');
4006         this.clearSelections();
4007         var el = this.dataName  ?
4008             this.el.child('.roo-tpl-' + this.dataName) :
4009             this.el; 
4010         
4011         el.dom.removeChild(this.nodes[index]);
4012         this.updateIndexes(index);
4013     },
4014
4015     /**
4016      * Refresh an individual node.
4017      * @param {Number} index
4018      */
4019     refreshNode : function(index){
4020         this.onUpdate(this.store, this.store.getAt(index));
4021     },
4022
4023     updateIndexes : function(startIndex, endIndex){
4024         var ns = this.nodes;
4025         startIndex = startIndex || 0;
4026         endIndex = endIndex || ns.length - 1;
4027         for(var i = startIndex; i <= endIndex; i++){
4028             ns[i].nodeIndex = i;
4029         }
4030     },
4031
4032     /**
4033      * Changes the data store this view uses and refresh the view.
4034      * @param {Store} store
4035      */
4036     setStore : function(store, initial){
4037         if(!initial && this.store){
4038             this.store.un("datachanged", this.refresh);
4039             this.store.un("add", this.onAdd);
4040             this.store.un("remove", this.onRemove);
4041             this.store.un("update", this.onUpdate);
4042             this.store.un("clear", this.refresh);
4043             this.store.un("beforeload", this.onBeforeLoad);
4044             this.store.un("load", this.onLoad);
4045             this.store.un("loadexception", this.onLoad);
4046         }
4047         if(store){
4048           
4049             store.on("datachanged", this.refresh, this);
4050             store.on("add", this.onAdd, this);
4051             store.on("remove", this.onRemove, this);
4052             store.on("update", this.onUpdate, this);
4053             store.on("clear", this.refresh, this);
4054             store.on("beforeload", this.onBeforeLoad, this);
4055             store.on("load", this.onLoad, this);
4056             store.on("loadexception", this.onLoad, this);
4057         }
4058         
4059         if(store){
4060             this.refresh();
4061         }
4062     },
4063     /**
4064      * onbeforeLoad - masks the loading area.
4065      *
4066      */
4067     onBeforeLoad : function(store,opts)
4068     {
4069          //Roo.log('onBeforeLoad');   
4070         if (!opts.add) {
4071             this.el.update("");
4072         }
4073         this.el.mask(this.mask ? this.mask : "Loading" ); 
4074     },
4075     onLoad : function ()
4076     {
4077         this.el.unmask();
4078     },
4079     
4080
4081     /**
4082      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4083      * @param {HTMLElement} node
4084      * @return {HTMLElement} The template node
4085      */
4086     findItemFromChild : function(node){
4087         var el = this.dataName  ?
4088             this.el.child('.roo-tpl-' + this.dataName,true) :
4089             this.el.dom; 
4090         
4091         if(!node || node.parentNode == el){
4092                     return node;
4093             }
4094             var p = node.parentNode;
4095             while(p && p != el){
4096             if(p.parentNode == el){
4097                 return p;
4098             }
4099             p = p.parentNode;
4100         }
4101             return null;
4102     },
4103
4104     /** @ignore */
4105     onClick : function(e){
4106         var item = this.findItemFromChild(e.getTarget());
4107         if(item){
4108             var index = this.indexOf(item);
4109             if(this.onItemClick(item, index, e) !== false){
4110                 this.fireEvent("click", this, index, item, e);
4111             }
4112         }else{
4113             this.clearSelections();
4114         }
4115     },
4116
4117     /** @ignore */
4118     onContextMenu : function(e){
4119         var item = this.findItemFromChild(e.getTarget());
4120         if(item){
4121             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4122         }
4123     },
4124
4125     /** @ignore */
4126     onDblClick : function(e){
4127         var item = this.findItemFromChild(e.getTarget());
4128         if(item){
4129             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4130         }
4131     },
4132
4133     onItemClick : function(item, index, e)
4134     {
4135         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4136             return false;
4137         }
4138         if (this.toggleSelect) {
4139             var m = this.isSelected(item) ? 'unselect' : 'select';
4140             //Roo.log(m);
4141             var _t = this;
4142             _t[m](item, true, false);
4143             return true;
4144         }
4145         if(this.multiSelect || this.singleSelect){
4146             if(this.multiSelect && e.shiftKey && this.lastSelection){
4147                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4148             }else{
4149                 this.select(item, this.multiSelect && e.ctrlKey);
4150                 this.lastSelection = item;
4151             }
4152             
4153             if(!this.tickable){
4154                 e.preventDefault();
4155             }
4156             
4157         }
4158         return true;
4159     },
4160
4161     /**
4162      * Get the number of selected nodes.
4163      * @return {Number}
4164      */
4165     getSelectionCount : function(){
4166         return this.selections.length;
4167     },
4168
4169     /**
4170      * Get the currently selected nodes.
4171      * @return {Array} An array of HTMLElements
4172      */
4173     getSelectedNodes : function(){
4174         return this.selections;
4175     },
4176
4177     /**
4178      * Get the indexes of the selected nodes.
4179      * @return {Array}
4180      */
4181     getSelectedIndexes : function(){
4182         var indexes = [], s = this.selections;
4183         for(var i = 0, len = s.length; i < len; i++){
4184             indexes.push(s[i].nodeIndex);
4185         }
4186         return indexes;
4187     },
4188
4189     /**
4190      * Clear all selections
4191      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4192      */
4193     clearSelections : function(suppressEvent){
4194         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4195             this.cmp.elements = this.selections;
4196             this.cmp.removeClass(this.selectedClass);
4197             this.selections = [];
4198             if(!suppressEvent){
4199                 this.fireEvent("selectionchange", this, this.selections);
4200             }
4201         }
4202     },
4203
4204     /**
4205      * Returns true if the passed node is selected
4206      * @param {HTMLElement/Number} node The node or node index
4207      * @return {Boolean}
4208      */
4209     isSelected : function(node){
4210         var s = this.selections;
4211         if(s.length < 1){
4212             return false;
4213         }
4214         node = this.getNode(node);
4215         return s.indexOf(node) !== -1;
4216     },
4217
4218     /**
4219      * Selects nodes.
4220      * @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
4221      * @param {Boolean} keepExisting (optional) true to keep existing selections
4222      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4223      */
4224     select : function(nodeInfo, keepExisting, suppressEvent){
4225         if(nodeInfo instanceof Array){
4226             if(!keepExisting){
4227                 this.clearSelections(true);
4228             }
4229             for(var i = 0, len = nodeInfo.length; i < len; i++){
4230                 this.select(nodeInfo[i], true, true);
4231             }
4232             return;
4233         } 
4234         var node = this.getNode(nodeInfo);
4235         if(!node || this.isSelected(node)){
4236             return; // already selected.
4237         }
4238         if(!keepExisting){
4239             this.clearSelections(true);
4240         }
4241         
4242         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4243             Roo.fly(node).addClass(this.selectedClass);
4244             this.selections.push(node);
4245             if(!suppressEvent){
4246                 this.fireEvent("selectionchange", this, this.selections);
4247             }
4248         }
4249         
4250         
4251     },
4252       /**
4253      * Unselects nodes.
4254      * @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
4255      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4256      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4257      */
4258     unselect : function(nodeInfo, keepExisting, suppressEvent)
4259     {
4260         if(nodeInfo instanceof Array){
4261             Roo.each(this.selections, function(s) {
4262                 this.unselect(s, nodeInfo);
4263             }, this);
4264             return;
4265         }
4266         var node = this.getNode(nodeInfo);
4267         if(!node || !this.isSelected(node)){
4268             //Roo.log("not selected");
4269             return; // not selected.
4270         }
4271         // fireevent???
4272         var ns = [];
4273         Roo.each(this.selections, function(s) {
4274             if (s == node ) {
4275                 Roo.fly(node).removeClass(this.selectedClass);
4276
4277                 return;
4278             }
4279             ns.push(s);
4280         },this);
4281         
4282         this.selections= ns;
4283         this.fireEvent("selectionchange", this, this.selections);
4284     },
4285
4286     /**
4287      * Gets a template node.
4288      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4289      * @return {HTMLElement} The node or null if it wasn't found
4290      */
4291     getNode : function(nodeInfo){
4292         if(typeof nodeInfo == "string"){
4293             return document.getElementById(nodeInfo);
4294         }else if(typeof nodeInfo == "number"){
4295             return this.nodes[nodeInfo];
4296         }
4297         return nodeInfo;
4298     },
4299
4300     /**
4301      * Gets a range template nodes.
4302      * @param {Number} startIndex
4303      * @param {Number} endIndex
4304      * @return {Array} An array of nodes
4305      */
4306     getNodes : function(start, end){
4307         var ns = this.nodes;
4308         start = start || 0;
4309         end = typeof end == "undefined" ? ns.length - 1 : end;
4310         var nodes = [];
4311         if(start <= end){
4312             for(var i = start; i <= end; i++){
4313                 nodes.push(ns[i]);
4314             }
4315         } else{
4316             for(var i = start; i >= end; i--){
4317                 nodes.push(ns[i]);
4318             }
4319         }
4320         return nodes;
4321     },
4322
4323     /**
4324      * Finds the index of the passed node
4325      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4326      * @return {Number} The index of the node or -1
4327      */
4328     indexOf : function(node){
4329         node = this.getNode(node);
4330         if(typeof node.nodeIndex == "number"){
4331             return node.nodeIndex;
4332         }
4333         var ns = this.nodes;
4334         for(var i = 0, len = ns.length; i < len; i++){
4335             if(ns[i] == node){
4336                 return i;
4337             }
4338         }
4339         return -1;
4340     }
4341 });
4342 /*
4343  * Based on:
4344  * Ext JS Library 1.1.1
4345  * Copyright(c) 2006-2007, Ext JS, LLC.
4346  *
4347  * Originally Released Under LGPL - original licence link has changed is not relivant.
4348  *
4349  * Fork - LGPL
4350  * <script type="text/javascript">
4351  */
4352
4353 /**
4354  * @class Roo.JsonView
4355  * @extends Roo.View
4356  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4357 <pre><code>
4358 var view = new Roo.JsonView({
4359     container: "my-element",
4360     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4361     multiSelect: true, 
4362     jsonRoot: "data" 
4363 });
4364
4365 // listen for node click?
4366 view.on("click", function(vw, index, node, e){
4367     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4368 });
4369
4370 // direct load of JSON data
4371 view.load("foobar.php");
4372
4373 // Example from my blog list
4374 var tpl = new Roo.Template(
4375     '&lt;div class="entry"&gt;' +
4376     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4377     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4378     "&lt;/div&gt;&lt;hr /&gt;"
4379 );
4380
4381 var moreView = new Roo.JsonView({
4382     container :  "entry-list", 
4383     template : tpl,
4384     jsonRoot: "posts"
4385 });
4386 moreView.on("beforerender", this.sortEntries, this);
4387 moreView.load({
4388     url: "/blog/get-posts.php",
4389     params: "allposts=true",
4390     text: "Loading Blog Entries..."
4391 });
4392 </code></pre>
4393
4394 * Note: old code is supported with arguments : (container, template, config)
4395
4396
4397  * @constructor
4398  * Create a new JsonView
4399  * 
4400  * @param {Object} config The config object
4401  * 
4402  */
4403 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4404     
4405     
4406     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4407
4408     var um = this.el.getUpdateManager();
4409     um.setRenderer(this);
4410     um.on("update", this.onLoad, this);
4411     um.on("failure", this.onLoadException, this);
4412
4413     /**
4414      * @event beforerender
4415      * Fires before rendering of the downloaded JSON data.
4416      * @param {Roo.JsonView} this
4417      * @param {Object} data The JSON data loaded
4418      */
4419     /**
4420      * @event load
4421      * Fires when data is loaded.
4422      * @param {Roo.JsonView} this
4423      * @param {Object} data The JSON data loaded
4424      * @param {Object} response The raw Connect response object
4425      */
4426     /**
4427      * @event loadexception
4428      * Fires when loading fails.
4429      * @param {Roo.JsonView} this
4430      * @param {Object} response The raw Connect response object
4431      */
4432     this.addEvents({
4433         'beforerender' : true,
4434         'load' : true,
4435         'loadexception' : true
4436     });
4437 };
4438 Roo.extend(Roo.JsonView, Roo.View, {
4439     /**
4440      * @type {String} The root property in the loaded JSON object that contains the data
4441      */
4442     jsonRoot : "",
4443
4444     /**
4445      * Refreshes the view.
4446      */
4447     refresh : function(){
4448         this.clearSelections();
4449         this.el.update("");
4450         var html = [];
4451         var o = this.jsonData;
4452         if(o && o.length > 0){
4453             for(var i = 0, len = o.length; i < len; i++){
4454                 var data = this.prepareData(o[i], i, o);
4455                 html[html.length] = this.tpl.apply(data);
4456             }
4457         }else{
4458             html.push(this.emptyText);
4459         }
4460         this.el.update(html.join(""));
4461         this.nodes = this.el.dom.childNodes;
4462         this.updateIndexes(0);
4463     },
4464
4465     /**
4466      * 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.
4467      * @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:
4468      <pre><code>
4469      view.load({
4470          url: "your-url.php",
4471          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4472          callback: yourFunction,
4473          scope: yourObject, //(optional scope)
4474          discardUrl: false,
4475          nocache: false,
4476          text: "Loading...",
4477          timeout: 30,
4478          scripts: false
4479      });
4480      </code></pre>
4481      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4482      * 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.
4483      * @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}
4484      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4485      * @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.
4486      */
4487     load : function(){
4488         var um = this.el.getUpdateManager();
4489         um.update.apply(um, arguments);
4490     },
4491
4492     // note - render is a standard framework call...
4493     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4494     render : function(el, response){
4495         
4496         this.clearSelections();
4497         this.el.update("");
4498         var o;
4499         try{
4500             if (response != '') {
4501                 o = Roo.util.JSON.decode(response.responseText);
4502                 if(this.jsonRoot){
4503                     
4504                     o = o[this.jsonRoot];
4505                 }
4506             }
4507         } catch(e){
4508         }
4509         /**
4510          * The current JSON data or null
4511          */
4512         this.jsonData = o;
4513         this.beforeRender();
4514         this.refresh();
4515     },
4516
4517 /**
4518  * Get the number of records in the current JSON dataset
4519  * @return {Number}
4520  */
4521     getCount : function(){
4522         return this.jsonData ? this.jsonData.length : 0;
4523     },
4524
4525 /**
4526  * Returns the JSON object for the specified node(s)
4527  * @param {HTMLElement/Array} node The node or an array of nodes
4528  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4529  * you get the JSON object for the node
4530  */
4531     getNodeData : function(node){
4532         if(node instanceof Array){
4533             var data = [];
4534             for(var i = 0, len = node.length; i < len; i++){
4535                 data.push(this.getNodeData(node[i]));
4536             }
4537             return data;
4538         }
4539         return this.jsonData[this.indexOf(node)] || null;
4540     },
4541
4542     beforeRender : function(){
4543         this.snapshot = this.jsonData;
4544         if(this.sortInfo){
4545             this.sort.apply(this, this.sortInfo);
4546         }
4547         this.fireEvent("beforerender", this, this.jsonData);
4548     },
4549
4550     onLoad : function(el, o){
4551         this.fireEvent("load", this, this.jsonData, o);
4552     },
4553
4554     onLoadException : function(el, o){
4555         this.fireEvent("loadexception", this, o);
4556     },
4557
4558 /**
4559  * Filter the data by a specific property.
4560  * @param {String} property A property on your JSON objects
4561  * @param {String/RegExp} value Either string that the property values
4562  * should start with, or a RegExp to test against the property
4563  */
4564     filter : function(property, value){
4565         if(this.jsonData){
4566             var data = [];
4567             var ss = this.snapshot;
4568             if(typeof value == "string"){
4569                 var vlen = value.length;
4570                 if(vlen == 0){
4571                     this.clearFilter();
4572                     return;
4573                 }
4574                 value = value.toLowerCase();
4575                 for(var i = 0, len = ss.length; i < len; i++){
4576                     var o = ss[i];
4577                     if(o[property].substr(0, vlen).toLowerCase() == value){
4578                         data.push(o);
4579                     }
4580                 }
4581             } else if(value.exec){ // regex?
4582                 for(var i = 0, len = ss.length; i < len; i++){
4583                     var o = ss[i];
4584                     if(value.test(o[property])){
4585                         data.push(o);
4586                     }
4587                 }
4588             } else{
4589                 return;
4590             }
4591             this.jsonData = data;
4592             this.refresh();
4593         }
4594     },
4595
4596 /**
4597  * Filter by a function. The passed function will be called with each
4598  * object in the current dataset. If the function returns true the value is kept,
4599  * otherwise it is filtered.
4600  * @param {Function} fn
4601  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4602  */
4603     filterBy : function(fn, scope){
4604         if(this.jsonData){
4605             var data = [];
4606             var ss = this.snapshot;
4607             for(var i = 0, len = ss.length; i < len; i++){
4608                 var o = ss[i];
4609                 if(fn.call(scope || this, o)){
4610                     data.push(o);
4611                 }
4612             }
4613             this.jsonData = data;
4614             this.refresh();
4615         }
4616     },
4617
4618 /**
4619  * Clears the current filter.
4620  */
4621     clearFilter : function(){
4622         if(this.snapshot && this.jsonData != this.snapshot){
4623             this.jsonData = this.snapshot;
4624             this.refresh();
4625         }
4626     },
4627
4628
4629 /**
4630  * Sorts the data for this view and refreshes it.
4631  * @param {String} property A property on your JSON objects to sort on
4632  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4633  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4634  */
4635     sort : function(property, dir, sortType){
4636         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4637         if(this.jsonData){
4638             var p = property;
4639             var dsc = dir && dir.toLowerCase() == "desc";
4640             var f = function(o1, o2){
4641                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4642                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4643                 ;
4644                 if(v1 < v2){
4645                     return dsc ? +1 : -1;
4646                 } else if(v1 > v2){
4647                     return dsc ? -1 : +1;
4648                 } else{
4649                     return 0;
4650                 }
4651             };
4652             this.jsonData.sort(f);
4653             this.refresh();
4654             if(this.jsonData != this.snapshot){
4655                 this.snapshot.sort(f);
4656             }
4657         }
4658     }
4659 });/*
4660  * Based on:
4661  * Ext JS Library 1.1.1
4662  * Copyright(c) 2006-2007, Ext JS, LLC.
4663  *
4664  * Originally Released Under LGPL - original licence link has changed is not relivant.
4665  *
4666  * Fork - LGPL
4667  * <script type="text/javascript">
4668  */
4669  
4670
4671 /**
4672  * @class Roo.ColorPalette
4673  * @extends Roo.Component
4674  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4675  * Here's an example of typical usage:
4676  * <pre><code>
4677 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4678 cp.render('my-div');
4679
4680 cp.on('select', function(palette, selColor){
4681     // do something with selColor
4682 });
4683 </code></pre>
4684  * @constructor
4685  * Create a new ColorPalette
4686  * @param {Object} config The config object
4687  */
4688 Roo.ColorPalette = function(config){
4689     Roo.ColorPalette.superclass.constructor.call(this, config);
4690     this.addEvents({
4691         /**
4692              * @event select
4693              * Fires when a color is selected
4694              * @param {ColorPalette} this
4695              * @param {String} color The 6-digit color hex code (without the # symbol)
4696              */
4697         select: true
4698     });
4699
4700     if(this.handler){
4701         this.on("select", this.handler, this.scope, true);
4702     }
4703 };
4704 Roo.extend(Roo.ColorPalette, Roo.Component, {
4705     /**
4706      * @cfg {String} itemCls
4707      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4708      */
4709     itemCls : "x-color-palette",
4710     /**
4711      * @cfg {String} value
4712      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4713      * the hex codes are case-sensitive.
4714      */
4715     value : null,
4716     clickEvent:'click',
4717     // private
4718     ctype: "Roo.ColorPalette",
4719
4720     /**
4721      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4722      */
4723     allowReselect : false,
4724
4725     /**
4726      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4727      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4728      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4729      * of colors with the width setting until the box is symmetrical.</p>
4730      * <p>You can override individual colors if needed:</p>
4731      * <pre><code>
4732 var cp = new Roo.ColorPalette();
4733 cp.colors[0] = "FF0000";  // change the first box to red
4734 </code></pre>
4735
4736 Or you can provide a custom array of your own for complete control:
4737 <pre><code>
4738 var cp = new Roo.ColorPalette();
4739 cp.colors = ["000000", "993300", "333300"];
4740 </code></pre>
4741      * @type Array
4742      */
4743     colors : [
4744         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4745         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4746         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4747         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4748         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4749     ],
4750
4751     // private
4752     onRender : function(container, position){
4753         var t = new Roo.MasterTemplate(
4754             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4755         );
4756         var c = this.colors;
4757         for(var i = 0, len = c.length; i < len; i++){
4758             t.add([c[i]]);
4759         }
4760         var el = document.createElement("div");
4761         el.className = this.itemCls;
4762         t.overwrite(el);
4763         container.dom.insertBefore(el, position);
4764         this.el = Roo.get(el);
4765         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4766         if(this.clickEvent != 'click'){
4767             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4768         }
4769     },
4770
4771     // private
4772     afterRender : function(){
4773         Roo.ColorPalette.superclass.afterRender.call(this);
4774         if(this.value){
4775             var s = this.value;
4776             this.value = null;
4777             this.select(s);
4778         }
4779     },
4780
4781     // private
4782     handleClick : function(e, t){
4783         e.preventDefault();
4784         if(!this.disabled){
4785             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4786             this.select(c.toUpperCase());
4787         }
4788     },
4789
4790     /**
4791      * Selects the specified color in the palette (fires the select event)
4792      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4793      */
4794     select : function(color){
4795         color = color.replace("#", "");
4796         if(color != this.value || this.allowReselect){
4797             var el = this.el;
4798             if(this.value){
4799                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4800             }
4801             el.child("a.color-"+color).addClass("x-color-palette-sel");
4802             this.value = color;
4803             this.fireEvent("select", this, color);
4804         }
4805     }
4806 });/*
4807  * Based on:
4808  * Ext JS Library 1.1.1
4809  * Copyright(c) 2006-2007, Ext JS, LLC.
4810  *
4811  * Originally Released Under LGPL - original licence link has changed is not relivant.
4812  *
4813  * Fork - LGPL
4814  * <script type="text/javascript">
4815  */
4816  
4817 /**
4818  * @class Roo.DatePicker
4819  * @extends Roo.Component
4820  * Simple date picker class.
4821  * @constructor
4822  * Create a new DatePicker
4823  * @param {Object} config The config object
4824  */
4825 Roo.DatePicker = function(config){
4826     Roo.DatePicker.superclass.constructor.call(this, config);
4827
4828     this.value = config && config.value ?
4829                  config.value.clearTime() : new Date().clearTime();
4830
4831     this.addEvents({
4832         /**
4833              * @event select
4834              * Fires when a date is selected
4835              * @param {DatePicker} this
4836              * @param {Date} date The selected date
4837              */
4838         'select': true,
4839         /**
4840              * @event monthchange
4841              * Fires when the displayed month changes 
4842              * @param {DatePicker} this
4843              * @param {Date} date The selected month
4844              */
4845         'monthchange': true
4846     });
4847
4848     if(this.handler){
4849         this.on("select", this.handler,  this.scope || this);
4850     }
4851     // build the disabledDatesRE
4852     if(!this.disabledDatesRE && this.disabledDates){
4853         var dd = this.disabledDates;
4854         var re = "(?:";
4855         for(var i = 0; i < dd.length; i++){
4856             re += dd[i];
4857             if(i != dd.length-1) {
4858                 re += "|";
4859             }
4860         }
4861         this.disabledDatesRE = new RegExp(re + ")");
4862     }
4863 };
4864
4865 Roo.extend(Roo.DatePicker, Roo.Component, {
4866     /**
4867      * @cfg {String} todayText
4868      * The text to display on the button that selects the current date (defaults to "Today")
4869      */
4870     todayText : "Today",
4871     /**
4872      * @cfg {String} okText
4873      * The text to display on the ok button
4874      */
4875     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4876     /**
4877      * @cfg {String} cancelText
4878      * The text to display on the cancel button
4879      */
4880     cancelText : "Cancel",
4881     /**
4882      * @cfg {String} todayTip
4883      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4884      */
4885     todayTip : "{0} (Spacebar)",
4886     /**
4887      * @cfg {Date} minDate
4888      * Minimum allowable date (JavaScript date object, defaults to null)
4889      */
4890     minDate : null,
4891     /**
4892      * @cfg {Date} maxDate
4893      * Maximum allowable date (JavaScript date object, defaults to null)
4894      */
4895     maxDate : null,
4896     /**
4897      * @cfg {String} minText
4898      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4899      */
4900     minText : "This date is before the minimum date",
4901     /**
4902      * @cfg {String} maxText
4903      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4904      */
4905     maxText : "This date is after the maximum date",
4906     /**
4907      * @cfg {String} format
4908      * The default date format string which can be overriden for localization support.  The format must be
4909      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4910      */
4911     format : "m/d/y",
4912     /**
4913      * @cfg {Array} disabledDays
4914      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4915      */
4916     disabledDays : null,
4917     /**
4918      * @cfg {String} disabledDaysText
4919      * The tooltip to display when the date falls on a disabled day (defaults to "")
4920      */
4921     disabledDaysText : "",
4922     /**
4923      * @cfg {RegExp} disabledDatesRE
4924      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4925      */
4926     disabledDatesRE : null,
4927     /**
4928      * @cfg {String} disabledDatesText
4929      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4930      */
4931     disabledDatesText : "",
4932     /**
4933      * @cfg {Boolean} constrainToViewport
4934      * True to constrain the date picker to the viewport (defaults to true)
4935      */
4936     constrainToViewport : true,
4937     /**
4938      * @cfg {Array} monthNames
4939      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4940      */
4941     monthNames : Date.monthNames,
4942     /**
4943      * @cfg {Array} dayNames
4944      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4945      */
4946     dayNames : Date.dayNames,
4947     /**
4948      * @cfg {String} nextText
4949      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4950      */
4951     nextText: 'Next Month (Control+Right)',
4952     /**
4953      * @cfg {String} prevText
4954      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4955      */
4956     prevText: 'Previous Month (Control+Left)',
4957     /**
4958      * @cfg {String} monthYearText
4959      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4960      */
4961     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4962     /**
4963      * @cfg {Number} startDay
4964      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4965      */
4966     startDay : 0,
4967     /**
4968      * @cfg {Bool} showClear
4969      * Show a clear button (usefull for date form elements that can be blank.)
4970      */
4971     
4972     showClear: false,
4973     
4974     /**
4975      * Sets the value of the date field
4976      * @param {Date} value The date to set
4977      */
4978     setValue : function(value){
4979         var old = this.value;
4980         
4981         if (typeof(value) == 'string') {
4982          
4983             value = Date.parseDate(value, this.format);
4984         }
4985         if (!value) {
4986             value = new Date();
4987         }
4988         
4989         this.value = value.clearTime(true);
4990         if(this.el){
4991             this.update(this.value);
4992         }
4993     },
4994
4995     /**
4996      * Gets the current selected value of the date field
4997      * @return {Date} The selected date
4998      */
4999     getValue : function(){
5000         return this.value;
5001     },
5002
5003     // private
5004     focus : function(){
5005         if(this.el){
5006             this.update(this.activeDate);
5007         }
5008     },
5009
5010     // privateval
5011     onRender : function(container, position){
5012         
5013         var m = [
5014              '<table cellspacing="0">',
5015                 '<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>',
5016                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5017         var dn = this.dayNames;
5018         for(var i = 0; i < 7; i++){
5019             var d = this.startDay+i;
5020             if(d > 6){
5021                 d = d-7;
5022             }
5023             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5024         }
5025         m[m.length] = "</tr></thead><tbody><tr>";
5026         for(var i = 0; i < 42; i++) {
5027             if(i % 7 == 0 && i != 0){
5028                 m[m.length] = "</tr><tr>";
5029             }
5030             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5031         }
5032         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5033             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5034
5035         var el = document.createElement("div");
5036         el.className = "x-date-picker";
5037         el.innerHTML = m.join("");
5038
5039         container.dom.insertBefore(el, position);
5040
5041         this.el = Roo.get(el);
5042         this.eventEl = Roo.get(el.firstChild);
5043
5044         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5045             handler: this.showPrevMonth,
5046             scope: this,
5047             preventDefault:true,
5048             stopDefault:true
5049         });
5050
5051         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5052             handler: this.showNextMonth,
5053             scope: this,
5054             preventDefault:true,
5055             stopDefault:true
5056         });
5057
5058         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5059
5060         this.monthPicker = this.el.down('div.x-date-mp');
5061         this.monthPicker.enableDisplayMode('block');
5062         
5063         var kn = new Roo.KeyNav(this.eventEl, {
5064             "left" : function(e){
5065                 e.ctrlKey ?
5066                     this.showPrevMonth() :
5067                     this.update(this.activeDate.add("d", -1));
5068             },
5069
5070             "right" : function(e){
5071                 e.ctrlKey ?
5072                     this.showNextMonth() :
5073                     this.update(this.activeDate.add("d", 1));
5074             },
5075
5076             "up" : function(e){
5077                 e.ctrlKey ?
5078                     this.showNextYear() :
5079                     this.update(this.activeDate.add("d", -7));
5080             },
5081
5082             "down" : function(e){
5083                 e.ctrlKey ?
5084                     this.showPrevYear() :
5085                     this.update(this.activeDate.add("d", 7));
5086             },
5087
5088             "pageUp" : function(e){
5089                 this.showNextMonth();
5090             },
5091
5092             "pageDown" : function(e){
5093                 this.showPrevMonth();
5094             },
5095
5096             "enter" : function(e){
5097                 e.stopPropagation();
5098                 return true;
5099             },
5100
5101             scope : this
5102         });
5103
5104         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5105
5106         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5107
5108         this.el.unselectable();
5109         
5110         this.cells = this.el.select("table.x-date-inner tbody td");
5111         this.textNodes = this.el.query("table.x-date-inner tbody span");
5112
5113         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5114             text: "&#160;",
5115             tooltip: this.monthYearText
5116         });
5117
5118         this.mbtn.on('click', this.showMonthPicker, this);
5119         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5120
5121
5122         var today = (new Date()).dateFormat(this.format);
5123         
5124         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5125         if (this.showClear) {
5126             baseTb.add( new Roo.Toolbar.Fill());
5127         }
5128         baseTb.add({
5129             text: String.format(this.todayText, today),
5130             tooltip: String.format(this.todayTip, today),
5131             handler: this.selectToday,
5132             scope: this
5133         });
5134         
5135         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5136             
5137         //});
5138         if (this.showClear) {
5139             
5140             baseTb.add( new Roo.Toolbar.Fill());
5141             baseTb.add({
5142                 text: '&#160;',
5143                 cls: 'x-btn-icon x-btn-clear',
5144                 handler: function() {
5145                     //this.value = '';
5146                     this.fireEvent("select", this, '');
5147                 },
5148                 scope: this
5149             });
5150         }
5151         
5152         
5153         if(Roo.isIE){
5154             this.el.repaint();
5155         }
5156         this.update(this.value);
5157     },
5158
5159     createMonthPicker : function(){
5160         if(!this.monthPicker.dom.firstChild){
5161             var buf = ['<table border="0" cellspacing="0">'];
5162             for(var i = 0; i < 6; i++){
5163                 buf.push(
5164                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5165                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5166                     i == 0 ?
5167                     '<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>' :
5168                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5169                 );
5170             }
5171             buf.push(
5172                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5173                     this.okText,
5174                     '</button><button type="button" class="x-date-mp-cancel">',
5175                     this.cancelText,
5176                     '</button></td></tr>',
5177                 '</table>'
5178             );
5179             this.monthPicker.update(buf.join(''));
5180             this.monthPicker.on('click', this.onMonthClick, this);
5181             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5182
5183             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5184             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5185
5186             this.mpMonths.each(function(m, a, i){
5187                 i += 1;
5188                 if((i%2) == 0){
5189                     m.dom.xmonth = 5 + Math.round(i * .5);
5190                 }else{
5191                     m.dom.xmonth = Math.round((i-1) * .5);
5192                 }
5193             });
5194         }
5195     },
5196
5197     showMonthPicker : function(){
5198         this.createMonthPicker();
5199         var size = this.el.getSize();
5200         this.monthPicker.setSize(size);
5201         this.monthPicker.child('table').setSize(size);
5202
5203         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5204         this.updateMPMonth(this.mpSelMonth);
5205         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5206         this.updateMPYear(this.mpSelYear);
5207
5208         this.monthPicker.slideIn('t', {duration:.2});
5209     },
5210
5211     updateMPYear : function(y){
5212         this.mpyear = y;
5213         var ys = this.mpYears.elements;
5214         for(var i = 1; i <= 10; i++){
5215             var td = ys[i-1], y2;
5216             if((i%2) == 0){
5217                 y2 = y + Math.round(i * .5);
5218                 td.firstChild.innerHTML = y2;
5219                 td.xyear = y2;
5220             }else{
5221                 y2 = y - (5-Math.round(i * .5));
5222                 td.firstChild.innerHTML = y2;
5223                 td.xyear = y2;
5224             }
5225             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5226         }
5227     },
5228
5229     updateMPMonth : function(sm){
5230         this.mpMonths.each(function(m, a, i){
5231             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5232         });
5233     },
5234
5235     selectMPMonth: function(m){
5236         
5237     },
5238
5239     onMonthClick : function(e, t){
5240         e.stopEvent();
5241         var el = new Roo.Element(t), pn;
5242         if(el.is('button.x-date-mp-cancel')){
5243             this.hideMonthPicker();
5244         }
5245         else if(el.is('button.x-date-mp-ok')){
5246             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5247             this.hideMonthPicker();
5248         }
5249         else if(pn = el.up('td.x-date-mp-month', 2)){
5250             this.mpMonths.removeClass('x-date-mp-sel');
5251             pn.addClass('x-date-mp-sel');
5252             this.mpSelMonth = pn.dom.xmonth;
5253         }
5254         else if(pn = el.up('td.x-date-mp-year', 2)){
5255             this.mpYears.removeClass('x-date-mp-sel');
5256             pn.addClass('x-date-mp-sel');
5257             this.mpSelYear = pn.dom.xyear;
5258         }
5259         else if(el.is('a.x-date-mp-prev')){
5260             this.updateMPYear(this.mpyear-10);
5261         }
5262         else if(el.is('a.x-date-mp-next')){
5263             this.updateMPYear(this.mpyear+10);
5264         }
5265     },
5266
5267     onMonthDblClick : function(e, t){
5268         e.stopEvent();
5269         var el = new Roo.Element(t), pn;
5270         if(pn = el.up('td.x-date-mp-month', 2)){
5271             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5272             this.hideMonthPicker();
5273         }
5274         else if(pn = el.up('td.x-date-mp-year', 2)){
5275             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5276             this.hideMonthPicker();
5277         }
5278     },
5279
5280     hideMonthPicker : function(disableAnim){
5281         if(this.monthPicker){
5282             if(disableAnim === true){
5283                 this.monthPicker.hide();
5284             }else{
5285                 this.monthPicker.slideOut('t', {duration:.2});
5286             }
5287         }
5288     },
5289
5290     // private
5291     showPrevMonth : function(e){
5292         this.update(this.activeDate.add("mo", -1));
5293     },
5294
5295     // private
5296     showNextMonth : function(e){
5297         this.update(this.activeDate.add("mo", 1));
5298     },
5299
5300     // private
5301     showPrevYear : function(){
5302         this.update(this.activeDate.add("y", -1));
5303     },
5304
5305     // private
5306     showNextYear : function(){
5307         this.update(this.activeDate.add("y", 1));
5308     },
5309
5310     // private
5311     handleMouseWheel : function(e){
5312         var delta = e.getWheelDelta();
5313         if(delta > 0){
5314             this.showPrevMonth();
5315             e.stopEvent();
5316         } else if(delta < 0){
5317             this.showNextMonth();
5318             e.stopEvent();
5319         }
5320     },
5321
5322     // private
5323     handleDateClick : function(e, t){
5324         e.stopEvent();
5325         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5326             this.setValue(new Date(t.dateValue));
5327             this.fireEvent("select", this, this.value);
5328         }
5329     },
5330
5331     // private
5332     selectToday : function(){
5333         this.setValue(new Date().clearTime());
5334         this.fireEvent("select", this, this.value);
5335     },
5336
5337     // private
5338     update : function(date)
5339     {
5340         var vd = this.activeDate;
5341         this.activeDate = date;
5342         if(vd && this.el){
5343             var t = date.getTime();
5344             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5345                 this.cells.removeClass("x-date-selected");
5346                 this.cells.each(function(c){
5347                    if(c.dom.firstChild.dateValue == t){
5348                        c.addClass("x-date-selected");
5349                        setTimeout(function(){
5350                             try{c.dom.firstChild.focus();}catch(e){}
5351                        }, 50);
5352                        return false;
5353                    }
5354                 });
5355                 return;
5356             }
5357         }
5358         
5359         var days = date.getDaysInMonth();
5360         var firstOfMonth = date.getFirstDateOfMonth();
5361         var startingPos = firstOfMonth.getDay()-this.startDay;
5362
5363         if(startingPos <= this.startDay){
5364             startingPos += 7;
5365         }
5366
5367         var pm = date.add("mo", -1);
5368         var prevStart = pm.getDaysInMonth()-startingPos;
5369
5370         var cells = this.cells.elements;
5371         var textEls = this.textNodes;
5372         days += startingPos;
5373
5374         // convert everything to numbers so it's fast
5375         var day = 86400000;
5376         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5377         var today = new Date().clearTime().getTime();
5378         var sel = date.clearTime().getTime();
5379         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5380         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5381         var ddMatch = this.disabledDatesRE;
5382         var ddText = this.disabledDatesText;
5383         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5384         var ddaysText = this.disabledDaysText;
5385         var format = this.format;
5386
5387         var setCellClass = function(cal, cell){
5388             cell.title = "";
5389             var t = d.getTime();
5390             cell.firstChild.dateValue = t;
5391             if(t == today){
5392                 cell.className += " x-date-today";
5393                 cell.title = cal.todayText;
5394             }
5395             if(t == sel){
5396                 cell.className += " x-date-selected";
5397                 setTimeout(function(){
5398                     try{cell.firstChild.focus();}catch(e){}
5399                 }, 50);
5400             }
5401             // disabling
5402             if(t < min) {
5403                 cell.className = " x-date-disabled";
5404                 cell.title = cal.minText;
5405                 return;
5406             }
5407             if(t > max) {
5408                 cell.className = " x-date-disabled";
5409                 cell.title = cal.maxText;
5410                 return;
5411             }
5412             if(ddays){
5413                 if(ddays.indexOf(d.getDay()) != -1){
5414                     cell.title = ddaysText;
5415                     cell.className = " x-date-disabled";
5416                 }
5417             }
5418             if(ddMatch && format){
5419                 var fvalue = d.dateFormat(format);
5420                 if(ddMatch.test(fvalue)){
5421                     cell.title = ddText.replace("%0", fvalue);
5422                     cell.className = " x-date-disabled";
5423                 }
5424             }
5425         };
5426
5427         var i = 0;
5428         for(; i < startingPos; i++) {
5429             textEls[i].innerHTML = (++prevStart);
5430             d.setDate(d.getDate()+1);
5431             cells[i].className = "x-date-prevday";
5432             setCellClass(this, cells[i]);
5433         }
5434         for(; i < days; i++){
5435             intDay = i - startingPos + 1;
5436             textEls[i].innerHTML = (intDay);
5437             d.setDate(d.getDate()+1);
5438             cells[i].className = "x-date-active";
5439             setCellClass(this, cells[i]);
5440         }
5441         var extraDays = 0;
5442         for(; i < 42; i++) {
5443              textEls[i].innerHTML = (++extraDays);
5444              d.setDate(d.getDate()+1);
5445              cells[i].className = "x-date-nextday";
5446              setCellClass(this, cells[i]);
5447         }
5448
5449         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5450         this.fireEvent('monthchange', this, date);
5451         
5452         if(!this.internalRender){
5453             var main = this.el.dom.firstChild;
5454             var w = main.offsetWidth;
5455             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5456             Roo.fly(main).setWidth(w);
5457             this.internalRender = true;
5458             // opera does not respect the auto grow header center column
5459             // then, after it gets a width opera refuses to recalculate
5460             // without a second pass
5461             if(Roo.isOpera && !this.secondPass){
5462                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5463                 this.secondPass = true;
5464                 this.update.defer(10, this, [date]);
5465             }
5466         }
5467         
5468         
5469     }
5470 });        /*
5471  * Based on:
5472  * Ext JS Library 1.1.1
5473  * Copyright(c) 2006-2007, Ext JS, LLC.
5474  *
5475  * Originally Released Under LGPL - original licence link has changed is not relivant.
5476  *
5477  * Fork - LGPL
5478  * <script type="text/javascript">
5479  */
5480 /**
5481  * @class Roo.TabPanel
5482  * @extends Roo.util.Observable
5483  * A lightweight tab container.
5484  * <br><br>
5485  * Usage:
5486  * <pre><code>
5487 // basic tabs 1, built from existing content
5488 var tabs = new Roo.TabPanel("tabs1");
5489 tabs.addTab("script", "View Script");
5490 tabs.addTab("markup", "View Markup");
5491 tabs.activate("script");
5492
5493 // more advanced tabs, built from javascript
5494 var jtabs = new Roo.TabPanel("jtabs");
5495 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5496
5497 // set up the UpdateManager
5498 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5499 var updater = tab2.getUpdateManager();
5500 updater.setDefaultUrl("ajax1.htm");
5501 tab2.on('activate', updater.refresh, updater, true);
5502
5503 // Use setUrl for Ajax loading
5504 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5505 tab3.setUrl("ajax2.htm", null, true);
5506
5507 // Disabled tab
5508 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5509 tab4.disable();
5510
5511 jtabs.activate("jtabs-1");
5512  * </code></pre>
5513  * @constructor
5514  * Create a new TabPanel.
5515  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5516  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5517  */
5518 Roo.TabPanel = function(container, config){
5519     /**
5520     * The container element for this TabPanel.
5521     * @type Roo.Element
5522     */
5523     this.el = Roo.get(container, true);
5524     if(config){
5525         if(typeof config == "boolean"){
5526             this.tabPosition = config ? "bottom" : "top";
5527         }else{
5528             Roo.apply(this, config);
5529         }
5530     }
5531     if(this.tabPosition == "bottom"){
5532         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5533         this.el.addClass("x-tabs-bottom");
5534     }
5535     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5536     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5537     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5538     if(Roo.isIE){
5539         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5540     }
5541     if(this.tabPosition != "bottom"){
5542         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5543          * @type Roo.Element
5544          */
5545         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5546         this.el.addClass("x-tabs-top");
5547     }
5548     this.items = [];
5549
5550     this.bodyEl.setStyle("position", "relative");
5551
5552     this.active = null;
5553     this.activateDelegate = this.activate.createDelegate(this);
5554
5555     this.addEvents({
5556         /**
5557          * @event tabchange
5558          * Fires when the active tab changes
5559          * @param {Roo.TabPanel} this
5560          * @param {Roo.TabPanelItem} activePanel The new active tab
5561          */
5562         "tabchange": true,
5563         /**
5564          * @event beforetabchange
5565          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5566          * @param {Roo.TabPanel} this
5567          * @param {Object} e Set cancel to true on this object to cancel the tab change
5568          * @param {Roo.TabPanelItem} tab The tab being changed to
5569          */
5570         "beforetabchange" : true
5571     });
5572
5573     Roo.EventManager.onWindowResize(this.onResize, this);
5574     this.cpad = this.el.getPadding("lr");
5575     this.hiddenCount = 0;
5576
5577
5578     // toolbar on the tabbar support...
5579     if (this.toolbar) {
5580         var tcfg = this.toolbar;
5581         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5582         this.toolbar = new Roo.Toolbar(tcfg);
5583         if (Roo.isSafari) {
5584             var tbl = tcfg.container.child('table', true);
5585             tbl.setAttribute('width', '100%');
5586         }
5587         
5588     }
5589    
5590
5591
5592     Roo.TabPanel.superclass.constructor.call(this);
5593 };
5594
5595 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5596     /*
5597      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5598      */
5599     tabPosition : "top",
5600     /*
5601      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5602      */
5603     currentTabWidth : 0,
5604     /*
5605      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5606      */
5607     minTabWidth : 40,
5608     /*
5609      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5610      */
5611     maxTabWidth : 250,
5612     /*
5613      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5614      */
5615     preferredTabWidth : 175,
5616     /*
5617      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5618      */
5619     resizeTabs : false,
5620     /*
5621      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5622      */
5623     monitorResize : true,
5624     /*
5625      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5626      */
5627     toolbar : false,
5628
5629     /**
5630      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5631      * @param {String} id The id of the div to use <b>or create</b>
5632      * @param {String} text The text for the tab
5633      * @param {String} content (optional) Content to put in the TabPanelItem body
5634      * @param {Boolean} closable (optional) True to create a close icon on the tab
5635      * @return {Roo.TabPanelItem} The created TabPanelItem
5636      */
5637     addTab : function(id, text, content, closable){
5638         var item = new Roo.TabPanelItem(this, id, text, closable);
5639         this.addTabItem(item);
5640         if(content){
5641             item.setContent(content);
5642         }
5643         return item;
5644     },
5645
5646     /**
5647      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5648      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5649      * @return {Roo.TabPanelItem}
5650      */
5651     getTab : function(id){
5652         return this.items[id];
5653     },
5654
5655     /**
5656      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5657      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5658      */
5659     hideTab : function(id){
5660         var t = this.items[id];
5661         if(!t.isHidden()){
5662            t.setHidden(true);
5663            this.hiddenCount++;
5664            this.autoSizeTabs();
5665         }
5666     },
5667
5668     /**
5669      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5670      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5671      */
5672     unhideTab : function(id){
5673         var t = this.items[id];
5674         if(t.isHidden()){
5675            t.setHidden(false);
5676            this.hiddenCount--;
5677            this.autoSizeTabs();
5678         }
5679     },
5680
5681     /**
5682      * Adds an existing {@link Roo.TabPanelItem}.
5683      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5684      */
5685     addTabItem : function(item){
5686         this.items[item.id] = item;
5687         this.items.push(item);
5688         if(this.resizeTabs){
5689            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5690            this.autoSizeTabs();
5691         }else{
5692             item.autoSize();
5693         }
5694     },
5695
5696     /**
5697      * Removes a {@link Roo.TabPanelItem}.
5698      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5699      */
5700     removeTab : function(id){
5701         var items = this.items;
5702         var tab = items[id];
5703         if(!tab) { return; }
5704         var index = items.indexOf(tab);
5705         if(this.active == tab && items.length > 1){
5706             var newTab = this.getNextAvailable(index);
5707             if(newTab) {
5708                 newTab.activate();
5709             }
5710         }
5711         this.stripEl.dom.removeChild(tab.pnode.dom);
5712         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5713             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5714         }
5715         items.splice(index, 1);
5716         delete this.items[tab.id];
5717         tab.fireEvent("close", tab);
5718         tab.purgeListeners();
5719         this.autoSizeTabs();
5720     },
5721
5722     getNextAvailable : function(start){
5723         var items = this.items;
5724         var index = start;
5725         // look for a next tab that will slide over to
5726         // replace the one being removed
5727         while(index < items.length){
5728             var item = items[++index];
5729             if(item && !item.isHidden()){
5730                 return item;
5731             }
5732         }
5733         // if one isn't found select the previous tab (on the left)
5734         index = start;
5735         while(index >= 0){
5736             var item = items[--index];
5737             if(item && !item.isHidden()){
5738                 return item;
5739             }
5740         }
5741         return null;
5742     },
5743
5744     /**
5745      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5746      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5747      */
5748     disableTab : function(id){
5749         var tab = this.items[id];
5750         if(tab && this.active != tab){
5751             tab.disable();
5752         }
5753     },
5754
5755     /**
5756      * Enables a {@link Roo.TabPanelItem} that is disabled.
5757      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5758      */
5759     enableTab : function(id){
5760         var tab = this.items[id];
5761         tab.enable();
5762     },
5763
5764     /**
5765      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5766      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5767      * @return {Roo.TabPanelItem} The TabPanelItem.
5768      */
5769     activate : function(id){
5770         var tab = this.items[id];
5771         if(!tab){
5772             return null;
5773         }
5774         if(tab == this.active || tab.disabled){
5775             return tab;
5776         }
5777         var e = {};
5778         this.fireEvent("beforetabchange", this, e, tab);
5779         if(e.cancel !== true && !tab.disabled){
5780             if(this.active){
5781                 this.active.hide();
5782             }
5783             this.active = this.items[id];
5784             this.active.show();
5785             this.fireEvent("tabchange", this, this.active);
5786         }
5787         return tab;
5788     },
5789
5790     /**
5791      * Gets the active {@link Roo.TabPanelItem}.
5792      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5793      */
5794     getActiveTab : function(){
5795         return this.active;
5796     },
5797
5798     /**
5799      * Updates the tab body element to fit the height of the container element
5800      * for overflow scrolling
5801      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5802      */
5803     syncHeight : function(targetHeight){
5804         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5805         var bm = this.bodyEl.getMargins();
5806         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5807         this.bodyEl.setHeight(newHeight);
5808         return newHeight;
5809     },
5810
5811     onResize : function(){
5812         if(this.monitorResize){
5813             this.autoSizeTabs();
5814         }
5815     },
5816
5817     /**
5818      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5819      */
5820     beginUpdate : function(){
5821         this.updating = true;
5822     },
5823
5824     /**
5825      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5826      */
5827     endUpdate : function(){
5828         this.updating = false;
5829         this.autoSizeTabs();
5830     },
5831
5832     /**
5833      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5834      */
5835     autoSizeTabs : function(){
5836         var count = this.items.length;
5837         var vcount = count - this.hiddenCount;
5838         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5839             return;
5840         }
5841         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5842         var availWidth = Math.floor(w / vcount);
5843         var b = this.stripBody;
5844         if(b.getWidth() > w){
5845             var tabs = this.items;
5846             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5847             if(availWidth < this.minTabWidth){
5848                 /*if(!this.sleft){    // incomplete scrolling code
5849                     this.createScrollButtons();
5850                 }
5851                 this.showScroll();
5852                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5853             }
5854         }else{
5855             if(this.currentTabWidth < this.preferredTabWidth){
5856                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5857             }
5858         }
5859     },
5860
5861     /**
5862      * Returns the number of tabs in this TabPanel.
5863      * @return {Number}
5864      */
5865      getCount : function(){
5866          return this.items.length;
5867      },
5868
5869     /**
5870      * Resizes all the tabs to the passed width
5871      * @param {Number} The new width
5872      */
5873     setTabWidth : function(width){
5874         this.currentTabWidth = width;
5875         for(var i = 0, len = this.items.length; i < len; i++) {
5876                 if(!this.items[i].isHidden()) {
5877                 this.items[i].setWidth(width);
5878             }
5879         }
5880     },
5881
5882     /**
5883      * Destroys this TabPanel
5884      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5885      */
5886     destroy : function(removeEl){
5887         Roo.EventManager.removeResizeListener(this.onResize, this);
5888         for(var i = 0, len = this.items.length; i < len; i++){
5889             this.items[i].purgeListeners();
5890         }
5891         if(removeEl === true){
5892             this.el.update("");
5893             this.el.remove();
5894         }
5895     }
5896 });
5897
5898 /**
5899  * @class Roo.TabPanelItem
5900  * @extends Roo.util.Observable
5901  * Represents an individual item (tab plus body) in a TabPanel.
5902  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5903  * @param {String} id The id of this TabPanelItem
5904  * @param {String} text The text for the tab of this TabPanelItem
5905  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5906  */
5907 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5908     /**
5909      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5910      * @type Roo.TabPanel
5911      */
5912     this.tabPanel = tabPanel;
5913     /**
5914      * The id for this TabPanelItem
5915      * @type String
5916      */
5917     this.id = id;
5918     /** @private */
5919     this.disabled = false;
5920     /** @private */
5921     this.text = text;
5922     /** @private */
5923     this.loaded = false;
5924     this.closable = closable;
5925
5926     /**
5927      * The body element for this TabPanelItem.
5928      * @type Roo.Element
5929      */
5930     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5931     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5932     this.bodyEl.setStyle("display", "block");
5933     this.bodyEl.setStyle("zoom", "1");
5934     this.hideAction();
5935
5936     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5937     /** @private */
5938     this.el = Roo.get(els.el, true);
5939     this.inner = Roo.get(els.inner, true);
5940     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5941     this.pnode = Roo.get(els.el.parentNode, true);
5942     this.el.on("mousedown", this.onTabMouseDown, this);
5943     this.el.on("click", this.onTabClick, this);
5944     /** @private */
5945     if(closable){
5946         var c = Roo.get(els.close, true);
5947         c.dom.title = this.closeText;
5948         c.addClassOnOver("close-over");
5949         c.on("click", this.closeClick, this);
5950      }
5951
5952     this.addEvents({
5953          /**
5954          * @event activate
5955          * Fires when this tab becomes the active tab.
5956          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5957          * @param {Roo.TabPanelItem} this
5958          */
5959         "activate": true,
5960         /**
5961          * @event beforeclose
5962          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5963          * @param {Roo.TabPanelItem} this
5964          * @param {Object} e Set cancel to true on this object to cancel the close.
5965          */
5966         "beforeclose": true,
5967         /**
5968          * @event close
5969          * Fires when this tab is closed.
5970          * @param {Roo.TabPanelItem} this
5971          */
5972          "close": true,
5973         /**
5974          * @event deactivate
5975          * Fires when this tab is no longer the active tab.
5976          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5977          * @param {Roo.TabPanelItem} this
5978          */
5979          "deactivate" : true
5980     });
5981     this.hidden = false;
5982
5983     Roo.TabPanelItem.superclass.constructor.call(this);
5984 };
5985
5986 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5987     purgeListeners : function(){
5988        Roo.util.Observable.prototype.purgeListeners.call(this);
5989        this.el.removeAllListeners();
5990     },
5991     /**
5992      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5993      */
5994     show : function(){
5995         this.pnode.addClass("on");
5996         this.showAction();
5997         if(Roo.isOpera){
5998             this.tabPanel.stripWrap.repaint();
5999         }
6000         this.fireEvent("activate", this.tabPanel, this);
6001     },
6002
6003     /**
6004      * Returns true if this tab is the active tab.
6005      * @return {Boolean}
6006      */
6007     isActive : function(){
6008         return this.tabPanel.getActiveTab() == this;
6009     },
6010
6011     /**
6012      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6013      */
6014     hide : function(){
6015         this.pnode.removeClass("on");
6016         this.hideAction();
6017         this.fireEvent("deactivate", this.tabPanel, this);
6018     },
6019
6020     hideAction : function(){
6021         this.bodyEl.hide();
6022         this.bodyEl.setStyle("position", "absolute");
6023         this.bodyEl.setLeft("-20000px");
6024         this.bodyEl.setTop("-20000px");
6025     },
6026
6027     showAction : function(){
6028         this.bodyEl.setStyle("position", "relative");
6029         this.bodyEl.setTop("");
6030         this.bodyEl.setLeft("");
6031         this.bodyEl.show();
6032     },
6033
6034     /**
6035      * Set the tooltip for the tab.
6036      * @param {String} tooltip The tab's tooltip
6037      */
6038     setTooltip : function(text){
6039         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6040             this.textEl.dom.qtip = text;
6041             this.textEl.dom.removeAttribute('title');
6042         }else{
6043             this.textEl.dom.title = text;
6044         }
6045     },
6046
6047     onTabClick : function(e){
6048         e.preventDefault();
6049         this.tabPanel.activate(this.id);
6050     },
6051
6052     onTabMouseDown : function(e){
6053         e.preventDefault();
6054         this.tabPanel.activate(this.id);
6055     },
6056
6057     getWidth : function(){
6058         return this.inner.getWidth();
6059     },
6060
6061     setWidth : function(width){
6062         var iwidth = width - this.pnode.getPadding("lr");
6063         this.inner.setWidth(iwidth);
6064         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6065         this.pnode.setWidth(width);
6066     },
6067
6068     /**
6069      * Show or hide the tab
6070      * @param {Boolean} hidden True to hide or false to show.
6071      */
6072     setHidden : function(hidden){
6073         this.hidden = hidden;
6074         this.pnode.setStyle("display", hidden ? "none" : "");
6075     },
6076
6077     /**
6078      * Returns true if this tab is "hidden"
6079      * @return {Boolean}
6080      */
6081     isHidden : function(){
6082         return this.hidden;
6083     },
6084
6085     /**
6086      * Returns the text for this tab
6087      * @return {String}
6088      */
6089     getText : function(){
6090         return this.text;
6091     },
6092
6093     autoSize : function(){
6094         //this.el.beginMeasure();
6095         this.textEl.setWidth(1);
6096         /*
6097          *  #2804 [new] Tabs in Roojs
6098          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6099          */
6100         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6101         //this.el.endMeasure();
6102     },
6103
6104     /**
6105      * Sets the text for the tab (Note: this also sets the tooltip text)
6106      * @param {String} text The tab's text and tooltip
6107      */
6108     setText : function(text){
6109         this.text = text;
6110         this.textEl.update(text);
6111         this.setTooltip(text);
6112         if(!this.tabPanel.resizeTabs){
6113             this.autoSize();
6114         }
6115     },
6116     /**
6117      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6118      */
6119     activate : function(){
6120         this.tabPanel.activate(this.id);
6121     },
6122
6123     /**
6124      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6125      */
6126     disable : function(){
6127         if(this.tabPanel.active != this){
6128             this.disabled = true;
6129             this.pnode.addClass("disabled");
6130         }
6131     },
6132
6133     /**
6134      * Enables this TabPanelItem if it was previously disabled.
6135      */
6136     enable : function(){
6137         this.disabled = false;
6138         this.pnode.removeClass("disabled");
6139     },
6140
6141     /**
6142      * Sets the content for this TabPanelItem.
6143      * @param {String} content The content
6144      * @param {Boolean} loadScripts true to look for and load scripts
6145      */
6146     setContent : function(content, loadScripts){
6147         this.bodyEl.update(content, loadScripts);
6148     },
6149
6150     /**
6151      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6152      * @return {Roo.UpdateManager} The UpdateManager
6153      */
6154     getUpdateManager : function(){
6155         return this.bodyEl.getUpdateManager();
6156     },
6157
6158     /**
6159      * Set a URL to be used to load the content for this TabPanelItem.
6160      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6161      * @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)
6162      * @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)
6163      * @return {Roo.UpdateManager} The UpdateManager
6164      */
6165     setUrl : function(url, params, loadOnce){
6166         if(this.refreshDelegate){
6167             this.un('activate', this.refreshDelegate);
6168         }
6169         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6170         this.on("activate", this.refreshDelegate);
6171         return this.bodyEl.getUpdateManager();
6172     },
6173
6174     /** @private */
6175     _handleRefresh : function(url, params, loadOnce){
6176         if(!loadOnce || !this.loaded){
6177             var updater = this.bodyEl.getUpdateManager();
6178             updater.update(url, params, this._setLoaded.createDelegate(this));
6179         }
6180     },
6181
6182     /**
6183      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6184      *   Will fail silently if the setUrl method has not been called.
6185      *   This does not activate the panel, just updates its content.
6186      */
6187     refresh : function(){
6188         if(this.refreshDelegate){
6189            this.loaded = false;
6190            this.refreshDelegate();
6191         }
6192     },
6193
6194     /** @private */
6195     _setLoaded : function(){
6196         this.loaded = true;
6197     },
6198
6199     /** @private */
6200     closeClick : function(e){
6201         var o = {};
6202         e.stopEvent();
6203         this.fireEvent("beforeclose", this, o);
6204         if(o.cancel !== true){
6205             this.tabPanel.removeTab(this.id);
6206         }
6207     },
6208     /**
6209      * The text displayed in the tooltip for the close icon.
6210      * @type String
6211      */
6212     closeText : "Close this tab"
6213 });
6214
6215 /** @private */
6216 Roo.TabPanel.prototype.createStrip = function(container){
6217     var strip = document.createElement("div");
6218     strip.className = "x-tabs-wrap";
6219     container.appendChild(strip);
6220     return strip;
6221 };
6222 /** @private */
6223 Roo.TabPanel.prototype.createStripList = function(strip){
6224     // div wrapper for retard IE
6225     // returns the "tr" element.
6226     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6227         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6228         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6229     return strip.firstChild.firstChild.firstChild.firstChild;
6230 };
6231 /** @private */
6232 Roo.TabPanel.prototype.createBody = function(container){
6233     var body = document.createElement("div");
6234     Roo.id(body, "tab-body");
6235     Roo.fly(body).addClass("x-tabs-body");
6236     container.appendChild(body);
6237     return body;
6238 };
6239 /** @private */
6240 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6241     var body = Roo.getDom(id);
6242     if(!body){
6243         body = document.createElement("div");
6244         body.id = id;
6245     }
6246     Roo.fly(body).addClass("x-tabs-item-body");
6247     bodyEl.insertBefore(body, bodyEl.firstChild);
6248     return body;
6249 };
6250 /** @private */
6251 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6252     var td = document.createElement("td");
6253     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6254     //stripEl.appendChild(td);
6255     if(closable){
6256         td.className = "x-tabs-closable";
6257         if(!this.closeTpl){
6258             this.closeTpl = new Roo.Template(
6259                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6260                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6261                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6262             );
6263         }
6264         var el = this.closeTpl.overwrite(td, {"text": text});
6265         var close = el.getElementsByTagName("div")[0];
6266         var inner = el.getElementsByTagName("em")[0];
6267         return {"el": el, "close": close, "inner": inner};
6268     } else {
6269         if(!this.tabTpl){
6270             this.tabTpl = new Roo.Template(
6271                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6272                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6273             );
6274         }
6275         var el = this.tabTpl.overwrite(td, {"text": text});
6276         var inner = el.getElementsByTagName("em")[0];
6277         return {"el": el, "inner": inner};
6278     }
6279 };/*
6280  * Based on:
6281  * Ext JS Library 1.1.1
6282  * Copyright(c) 2006-2007, Ext JS, LLC.
6283  *
6284  * Originally Released Under LGPL - original licence link has changed is not relivant.
6285  *
6286  * Fork - LGPL
6287  * <script type="text/javascript">
6288  */
6289
6290 /**
6291  * @class Roo.Button
6292  * @extends Roo.util.Observable
6293  * Simple Button class
6294  * @cfg {String} text The button text
6295  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6296  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6297  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6298  * @cfg {Object} scope The scope of the handler
6299  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6300  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6301  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6302  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6303  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6304  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6305    applies if enableToggle = true)
6306  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6307  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6308   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6309  * @constructor
6310  * Create a new button
6311  * @param {Object} config The config object
6312  */
6313 Roo.Button = function(renderTo, config)
6314 {
6315     if (!config) {
6316         config = renderTo;
6317         renderTo = config.renderTo || false;
6318     }
6319     
6320     Roo.apply(this, config);
6321     this.addEvents({
6322         /**
6323              * @event click
6324              * Fires when this button is clicked
6325              * @param {Button} this
6326              * @param {EventObject} e The click event
6327              */
6328             "click" : true,
6329         /**
6330              * @event toggle
6331              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6332              * @param {Button} this
6333              * @param {Boolean} pressed
6334              */
6335             "toggle" : true,
6336         /**
6337              * @event mouseover
6338              * Fires when the mouse hovers over the button
6339              * @param {Button} this
6340              * @param {Event} e The event object
6341              */
6342         'mouseover' : true,
6343         /**
6344              * @event mouseout
6345              * Fires when the mouse exits the button
6346              * @param {Button} this
6347              * @param {Event} e The event object
6348              */
6349         'mouseout': true,
6350          /**
6351              * @event render
6352              * Fires when the button is rendered
6353              * @param {Button} this
6354              */
6355         'render': true
6356     });
6357     if(this.menu){
6358         this.menu = Roo.menu.MenuMgr.get(this.menu);
6359     }
6360     // register listeners first!!  - so render can be captured..
6361     Roo.util.Observable.call(this);
6362     if(renderTo){
6363         this.render(renderTo);
6364     }
6365     
6366   
6367 };
6368
6369 Roo.extend(Roo.Button, Roo.util.Observable, {
6370     /**
6371      * 
6372      */
6373     
6374     /**
6375      * Read-only. True if this button is hidden
6376      * @type Boolean
6377      */
6378     hidden : false,
6379     /**
6380      * Read-only. True if this button is disabled
6381      * @type Boolean
6382      */
6383     disabled : false,
6384     /**
6385      * Read-only. True if this button is pressed (only if enableToggle = true)
6386      * @type Boolean
6387      */
6388     pressed : false,
6389
6390     /**
6391      * @cfg {Number} tabIndex 
6392      * The DOM tabIndex for this button (defaults to undefined)
6393      */
6394     tabIndex : undefined,
6395
6396     /**
6397      * @cfg {Boolean} enableToggle
6398      * True to enable pressed/not pressed toggling (defaults to false)
6399      */
6400     enableToggle: false,
6401     /**
6402      * @cfg {Roo.menu.Menu} menu
6403      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6404      */
6405     menu : undefined,
6406     /**
6407      * @cfg {String} menuAlign
6408      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6409      */
6410     menuAlign : "tl-bl?",
6411
6412     /**
6413      * @cfg {String} iconCls
6414      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6415      */
6416     iconCls : undefined,
6417     /**
6418      * @cfg {String} type
6419      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6420      */
6421     type : 'button',
6422
6423     // private
6424     menuClassTarget: 'tr',
6425
6426     /**
6427      * @cfg {String} clickEvent
6428      * The type of event to map to the button's event handler (defaults to 'click')
6429      */
6430     clickEvent : 'click',
6431
6432     /**
6433      * @cfg {Boolean} handleMouseEvents
6434      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6435      */
6436     handleMouseEvents : true,
6437
6438     /**
6439      * @cfg {String} tooltipType
6440      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6441      */
6442     tooltipType : 'qtip',
6443
6444     /**
6445      * @cfg {String} cls
6446      * A CSS class to apply to the button's main element.
6447      */
6448     
6449     /**
6450      * @cfg {Roo.Template} template (Optional)
6451      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6452      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6453      * require code modifications if required elements (e.g. a button) aren't present.
6454      */
6455
6456     // private
6457     render : function(renderTo){
6458         var btn;
6459         if(this.hideParent){
6460             this.parentEl = Roo.get(renderTo);
6461         }
6462         if(!this.dhconfig){
6463             if(!this.template){
6464                 if(!Roo.Button.buttonTemplate){
6465                     // hideous table template
6466                     Roo.Button.buttonTemplate = new Roo.Template(
6467                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6468                         '<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>',
6469                         "</tr></tbody></table>");
6470                 }
6471                 this.template = Roo.Button.buttonTemplate;
6472             }
6473             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6474             var btnEl = btn.child("button:first");
6475             btnEl.on('focus', this.onFocus, this);
6476             btnEl.on('blur', this.onBlur, this);
6477             if(this.cls){
6478                 btn.addClass(this.cls);
6479             }
6480             if(this.icon){
6481                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6482             }
6483             if(this.iconCls){
6484                 btnEl.addClass(this.iconCls);
6485                 if(!this.cls){
6486                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6487                 }
6488             }
6489             if(this.tabIndex !== undefined){
6490                 btnEl.dom.tabIndex = this.tabIndex;
6491             }
6492             if(this.tooltip){
6493                 if(typeof this.tooltip == 'object'){
6494                     Roo.QuickTips.tips(Roo.apply({
6495                           target: btnEl.id
6496                     }, this.tooltip));
6497                 } else {
6498                     btnEl.dom[this.tooltipType] = this.tooltip;
6499                 }
6500             }
6501         }else{
6502             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6503         }
6504         this.el = btn;
6505         if(this.id){
6506             this.el.dom.id = this.el.id = this.id;
6507         }
6508         if(this.menu){
6509             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6510             this.menu.on("show", this.onMenuShow, this);
6511             this.menu.on("hide", this.onMenuHide, this);
6512         }
6513         btn.addClass("x-btn");
6514         if(Roo.isIE && !Roo.isIE7){
6515             this.autoWidth.defer(1, this);
6516         }else{
6517             this.autoWidth();
6518         }
6519         if(this.handleMouseEvents){
6520             btn.on("mouseover", this.onMouseOver, this);
6521             btn.on("mouseout", this.onMouseOut, this);
6522             btn.on("mousedown", this.onMouseDown, this);
6523         }
6524         btn.on(this.clickEvent, this.onClick, this);
6525         //btn.on("mouseup", this.onMouseUp, this);
6526         if(this.hidden){
6527             this.hide();
6528         }
6529         if(this.disabled){
6530             this.disable();
6531         }
6532         Roo.ButtonToggleMgr.register(this);
6533         if(this.pressed){
6534             this.el.addClass("x-btn-pressed");
6535         }
6536         if(this.repeat){
6537             var repeater = new Roo.util.ClickRepeater(btn,
6538                 typeof this.repeat == "object" ? this.repeat : {}
6539             );
6540             repeater.on("click", this.onClick,  this);
6541         }
6542         
6543         this.fireEvent('render', this);
6544         
6545     },
6546     /**
6547      * Returns the button's underlying element
6548      * @return {Roo.Element} The element
6549      */
6550     getEl : function(){
6551         return this.el;  
6552     },
6553     
6554     /**
6555      * Destroys this Button and removes any listeners.
6556      */
6557     destroy : function(){
6558         Roo.ButtonToggleMgr.unregister(this);
6559         this.el.removeAllListeners();
6560         this.purgeListeners();
6561         this.el.remove();
6562     },
6563
6564     // private
6565     autoWidth : function(){
6566         if(this.el){
6567             this.el.setWidth("auto");
6568             if(Roo.isIE7 && Roo.isStrict){
6569                 var ib = this.el.child('button');
6570                 if(ib && ib.getWidth() > 20){
6571                     ib.clip();
6572                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6573                 }
6574             }
6575             if(this.minWidth){
6576                 if(this.hidden){
6577                     this.el.beginMeasure();
6578                 }
6579                 if(this.el.getWidth() < this.minWidth){
6580                     this.el.setWidth(this.minWidth);
6581                 }
6582                 if(this.hidden){
6583                     this.el.endMeasure();
6584                 }
6585             }
6586         }
6587     },
6588
6589     /**
6590      * Assigns this button's click handler
6591      * @param {Function} handler The function to call when the button is clicked
6592      * @param {Object} scope (optional) Scope for the function passed in
6593      */
6594     setHandler : function(handler, scope){
6595         this.handler = handler;
6596         this.scope = scope;  
6597     },
6598     
6599     /**
6600      * Sets this button's text
6601      * @param {String} text The button text
6602      */
6603     setText : function(text){
6604         this.text = text;
6605         if(this.el){
6606             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6607         }
6608         this.autoWidth();
6609     },
6610     
6611     /**
6612      * Gets the text for this button
6613      * @return {String} The button text
6614      */
6615     getText : function(){
6616         return this.text;  
6617     },
6618     
6619     /**
6620      * Show this button
6621      */
6622     show: function(){
6623         this.hidden = false;
6624         if(this.el){
6625             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6626         }
6627     },
6628     
6629     /**
6630      * Hide this button
6631      */
6632     hide: function(){
6633         this.hidden = true;
6634         if(this.el){
6635             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6636         }
6637     },
6638     
6639     /**
6640      * Convenience function for boolean show/hide
6641      * @param {Boolean} visible True to show, false to hide
6642      */
6643     setVisible: function(visible){
6644         if(visible) {
6645             this.show();
6646         }else{
6647             this.hide();
6648         }
6649     },
6650     /**
6651          * Similar to toggle, but does not trigger event.
6652          * @param {Boolean} state [required] Force a particular state
6653          */
6654         setPressed : function(state)
6655         {
6656             if(state != this.pressed){
6657             if(state){
6658                 this.el.addClass("x-btn-pressed");
6659                 this.pressed = true;
6660             }else{
6661                 this.el.removeClass("x-btn-pressed");
6662                 this.pressed = false;
6663             }
6664         }
6665         },
6666         
6667     /**
6668      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6669      * @param {Boolean} state (optional) Force a particular state
6670      */
6671     toggle : function(state){
6672         state = state === undefined ? !this.pressed : state;
6673         if(state != this.pressed){
6674             if(state){
6675                 this.el.addClass("x-btn-pressed");
6676                 this.pressed = true;
6677                 this.fireEvent("toggle", this, true);
6678             }else{
6679                 this.el.removeClass("x-btn-pressed");
6680                 this.pressed = false;
6681                 this.fireEvent("toggle", this, false);
6682             }
6683             if(this.toggleHandler){
6684                 this.toggleHandler.call(this.scope || this, this, state);
6685             }
6686         }
6687     },
6688     
6689         
6690         
6691     /**
6692      * Focus the button
6693      */
6694     focus : function(){
6695         this.el.child('button:first').focus();
6696     },
6697     
6698     /**
6699      * Disable this button
6700      */
6701     disable : function(){
6702         if(this.el){
6703             this.el.addClass("x-btn-disabled");
6704         }
6705         this.disabled = true;
6706     },
6707     
6708     /**
6709      * Enable this button
6710      */
6711     enable : function(){
6712         if(this.el){
6713             this.el.removeClass("x-btn-disabled");
6714         }
6715         this.disabled = false;
6716     },
6717
6718     /**
6719      * Convenience function for boolean enable/disable
6720      * @param {Boolean} enabled True to enable, false to disable
6721      */
6722     setDisabled : function(v){
6723         this[v !== true ? "enable" : "disable"]();
6724     },
6725
6726     // private
6727     onClick : function(e)
6728     {
6729         if(e){
6730             e.preventDefault();
6731         }
6732         if(e.button != 0){
6733             return;
6734         }
6735         if(!this.disabled){
6736             if(this.enableToggle){
6737                 this.toggle();
6738             }
6739             if(this.menu && !this.menu.isVisible()){
6740                 this.menu.show(this.el, this.menuAlign);
6741             }
6742             this.fireEvent("click", this, e);
6743             if(this.handler){
6744                 this.el.removeClass("x-btn-over");
6745                 this.handler.call(this.scope || this, this, e);
6746             }
6747         }
6748     },
6749     // private
6750     onMouseOver : function(e){
6751         if(!this.disabled){
6752             this.el.addClass("x-btn-over");
6753             this.fireEvent('mouseover', this, e);
6754         }
6755     },
6756     // private
6757     onMouseOut : function(e){
6758         if(!e.within(this.el,  true)){
6759             this.el.removeClass("x-btn-over");
6760             this.fireEvent('mouseout', this, e);
6761         }
6762     },
6763     // private
6764     onFocus : function(e){
6765         if(!this.disabled){
6766             this.el.addClass("x-btn-focus");
6767         }
6768     },
6769     // private
6770     onBlur : function(e){
6771         this.el.removeClass("x-btn-focus");
6772     },
6773     // private
6774     onMouseDown : function(e){
6775         if(!this.disabled && e.button == 0){
6776             this.el.addClass("x-btn-click");
6777             Roo.get(document).on('mouseup', this.onMouseUp, this);
6778         }
6779     },
6780     // private
6781     onMouseUp : function(e){
6782         if(e.button == 0){
6783             this.el.removeClass("x-btn-click");
6784             Roo.get(document).un('mouseup', this.onMouseUp, this);
6785         }
6786     },
6787     // private
6788     onMenuShow : function(e){
6789         this.el.addClass("x-btn-menu-active");
6790     },
6791     // private
6792     onMenuHide : function(e){
6793         this.el.removeClass("x-btn-menu-active");
6794     }   
6795 });
6796
6797 // Private utility class used by Button
6798 Roo.ButtonToggleMgr = function(){
6799    var groups = {};
6800    
6801    function toggleGroup(btn, state){
6802        if(state){
6803            var g = groups[btn.toggleGroup];
6804            for(var i = 0, l = g.length; i < l; i++){
6805                if(g[i] != btn){
6806                    g[i].toggle(false);
6807                }
6808            }
6809        }
6810    }
6811    
6812    return {
6813        register : function(btn){
6814            if(!btn.toggleGroup){
6815                return;
6816            }
6817            var g = groups[btn.toggleGroup];
6818            if(!g){
6819                g = groups[btn.toggleGroup] = [];
6820            }
6821            g.push(btn);
6822            btn.on("toggle", toggleGroup);
6823        },
6824        
6825        unregister : function(btn){
6826            if(!btn.toggleGroup){
6827                return;
6828            }
6829            var g = groups[btn.toggleGroup];
6830            if(g){
6831                g.remove(btn);
6832                btn.un("toggle", toggleGroup);
6833            }
6834        }
6835    };
6836 }();/*
6837  * Based on:
6838  * Ext JS Library 1.1.1
6839  * Copyright(c) 2006-2007, Ext JS, LLC.
6840  *
6841  * Originally Released Under LGPL - original licence link has changed is not relivant.
6842  *
6843  * Fork - LGPL
6844  * <script type="text/javascript">
6845  */
6846  
6847 /**
6848  * @class Roo.SplitButton
6849  * @extends Roo.Button
6850  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6851  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6852  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6853  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6854  * @cfg {String} arrowTooltip The title attribute of the arrow
6855  * @constructor
6856  * Create a new menu button
6857  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6858  * @param {Object} config The config object
6859  */
6860 Roo.SplitButton = function(renderTo, config){
6861     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6862     /**
6863      * @event arrowclick
6864      * Fires when this button's arrow is clicked
6865      * @param {SplitButton} this
6866      * @param {EventObject} e The click event
6867      */
6868     this.addEvents({"arrowclick":true});
6869 };
6870
6871 Roo.extend(Roo.SplitButton, Roo.Button, {
6872     render : function(renderTo){
6873         // this is one sweet looking template!
6874         var tpl = new Roo.Template(
6875             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6876             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6877             '<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>',
6878             "</tbody></table></td><td>",
6879             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6880             '<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>',
6881             "</tbody></table></td></tr></table>"
6882         );
6883         var btn = tpl.append(renderTo, [this.text, this.type], true);
6884         var btnEl = btn.child("button");
6885         if(this.cls){
6886             btn.addClass(this.cls);
6887         }
6888         if(this.icon){
6889             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6890         }
6891         if(this.iconCls){
6892             btnEl.addClass(this.iconCls);
6893             if(!this.cls){
6894                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6895             }
6896         }
6897         this.el = btn;
6898         if(this.handleMouseEvents){
6899             btn.on("mouseover", this.onMouseOver, this);
6900             btn.on("mouseout", this.onMouseOut, this);
6901             btn.on("mousedown", this.onMouseDown, this);
6902             btn.on("mouseup", this.onMouseUp, this);
6903         }
6904         btn.on(this.clickEvent, this.onClick, this);
6905         if(this.tooltip){
6906             if(typeof this.tooltip == 'object'){
6907                 Roo.QuickTips.tips(Roo.apply({
6908                       target: btnEl.id
6909                 }, this.tooltip));
6910             } else {
6911                 btnEl.dom[this.tooltipType] = this.tooltip;
6912             }
6913         }
6914         if(this.arrowTooltip){
6915             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6916         }
6917         if(this.hidden){
6918             this.hide();
6919         }
6920         if(this.disabled){
6921             this.disable();
6922         }
6923         if(this.pressed){
6924             this.el.addClass("x-btn-pressed");
6925         }
6926         if(Roo.isIE && !Roo.isIE7){
6927             this.autoWidth.defer(1, this);
6928         }else{
6929             this.autoWidth();
6930         }
6931         if(this.menu){
6932             this.menu.on("show", this.onMenuShow, this);
6933             this.menu.on("hide", this.onMenuHide, this);
6934         }
6935         this.fireEvent('render', this);
6936     },
6937
6938     // private
6939     autoWidth : function(){
6940         if(this.el){
6941             var tbl = this.el.child("table:first");
6942             var tbl2 = this.el.child("table:last");
6943             this.el.setWidth("auto");
6944             tbl.setWidth("auto");
6945             if(Roo.isIE7 && Roo.isStrict){
6946                 var ib = this.el.child('button:first');
6947                 if(ib && ib.getWidth() > 20){
6948                     ib.clip();
6949                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6950                 }
6951             }
6952             if(this.minWidth){
6953                 if(this.hidden){
6954                     this.el.beginMeasure();
6955                 }
6956                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6957                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6958                 }
6959                 if(this.hidden){
6960                     this.el.endMeasure();
6961                 }
6962             }
6963             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6964         } 
6965     },
6966     /**
6967      * Sets this button's click handler
6968      * @param {Function} handler The function to call when the button is clicked
6969      * @param {Object} scope (optional) Scope for the function passed above
6970      */
6971     setHandler : function(handler, scope){
6972         this.handler = handler;
6973         this.scope = scope;  
6974     },
6975     
6976     /**
6977      * Sets this button's arrow click handler
6978      * @param {Function} handler The function to call when the arrow is clicked
6979      * @param {Object} scope (optional) Scope for the function passed above
6980      */
6981     setArrowHandler : function(handler, scope){
6982         this.arrowHandler = handler;
6983         this.scope = scope;  
6984     },
6985     
6986     /**
6987      * Focus the button
6988      */
6989     focus : function(){
6990         if(this.el){
6991             this.el.child("button:first").focus();
6992         }
6993     },
6994
6995     // private
6996     onClick : function(e){
6997         e.preventDefault();
6998         if(!this.disabled){
6999             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7000                 if(this.menu && !this.menu.isVisible()){
7001                     this.menu.show(this.el, this.menuAlign);
7002                 }
7003                 this.fireEvent("arrowclick", this, e);
7004                 if(this.arrowHandler){
7005                     this.arrowHandler.call(this.scope || this, this, e);
7006                 }
7007             }else{
7008                 this.fireEvent("click", this, e);
7009                 if(this.handler){
7010                     this.handler.call(this.scope || this, this, e);
7011                 }
7012             }
7013         }
7014     },
7015     // private
7016     onMouseDown : function(e){
7017         if(!this.disabled){
7018             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7019         }
7020     },
7021     // private
7022     onMouseUp : function(e){
7023         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7024     }   
7025 });
7026
7027
7028 // backwards compat
7029 Roo.MenuButton = Roo.SplitButton;/*
7030  * Based on:
7031  * Ext JS Library 1.1.1
7032  * Copyright(c) 2006-2007, Ext JS, LLC.
7033  *
7034  * Originally Released Under LGPL - original licence link has changed is not relivant.
7035  *
7036  * Fork - LGPL
7037  * <script type="text/javascript">
7038  */
7039
7040 /**
7041  * @class Roo.Toolbar
7042  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
7043  * Basic Toolbar class.
7044  * @constructor
7045  * Creates a new Toolbar
7046  * @param {Object} container The config object
7047  */ 
7048 Roo.Toolbar = function(container, buttons, config)
7049 {
7050     /// old consturctor format still supported..
7051     if(container instanceof Array){ // omit the container for later rendering
7052         buttons = container;
7053         config = buttons;
7054         container = null;
7055     }
7056     if (typeof(container) == 'object' && container.xtype) {
7057         config = container;
7058         container = config.container;
7059         buttons = config.buttons || []; // not really - use items!!
7060     }
7061     var xitems = [];
7062     if (config && config.items) {
7063         xitems = config.items;
7064         delete config.items;
7065     }
7066     Roo.apply(this, config);
7067     this.buttons = buttons;
7068     
7069     if(container){
7070         this.render(container);
7071     }
7072     this.xitems = xitems;
7073     Roo.each(xitems, function(b) {
7074         this.add(b);
7075     }, this);
7076     
7077 };
7078
7079 Roo.Toolbar.prototype = {
7080     /**
7081      * @cfg {Array} items
7082      * array of button configs or elements to add (will be converted to a MixedCollection)
7083      */
7084     items: false,
7085     /**
7086      * @cfg {String/HTMLElement/Element} container
7087      * The id or element that will contain the toolbar
7088      */
7089     // private
7090     render : function(ct){
7091         this.el = Roo.get(ct);
7092         if(this.cls){
7093             this.el.addClass(this.cls);
7094         }
7095         // using a table allows for vertical alignment
7096         // 100% width is needed by Safari...
7097         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7098         this.tr = this.el.child("tr", true);
7099         var autoId = 0;
7100         this.items = new Roo.util.MixedCollection(false, function(o){
7101             return o.id || ("item" + (++autoId));
7102         });
7103         if(this.buttons){
7104             this.add.apply(this, this.buttons);
7105             delete this.buttons;
7106         }
7107     },
7108
7109     /**
7110      * Adds element(s) to the toolbar -- this function takes a variable number of 
7111      * arguments of mixed type and adds them to the toolbar.
7112      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7113      * <ul>
7114      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7115      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7116      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7117      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7118      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7119      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7120      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7121      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7122      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7123      * </ul>
7124      * @param {Mixed} arg2
7125      * @param {Mixed} etc.
7126      */
7127     add : function(){
7128         var a = arguments, l = a.length;
7129         for(var i = 0; i < l; i++){
7130             this._add(a[i]);
7131         }
7132     },
7133     // private..
7134     _add : function(el) {
7135         
7136         if (el.xtype) {
7137             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7138         }
7139         
7140         if (el.applyTo){ // some kind of form field
7141             return this.addField(el);
7142         } 
7143         if (el.render){ // some kind of Toolbar.Item
7144             return this.addItem(el);
7145         }
7146         if (typeof el == "string"){ // string
7147             if(el == "separator" || el == "-"){
7148                 return this.addSeparator();
7149             }
7150             if (el == " "){
7151                 return this.addSpacer();
7152             }
7153             if(el == "->"){
7154                 return this.addFill();
7155             }
7156             return this.addText(el);
7157             
7158         }
7159         if(el.tagName){ // element
7160             return this.addElement(el);
7161         }
7162         if(typeof el == "object"){ // must be button config?
7163             return this.addButton(el);
7164         }
7165         // and now what?!?!
7166         return false;
7167         
7168     },
7169     
7170     /**
7171      * Add an Xtype element
7172      * @param {Object} xtype Xtype Object
7173      * @return {Object} created Object
7174      */
7175     addxtype : function(e){
7176         return this.add(e);  
7177     },
7178     
7179     /**
7180      * Returns the Element for this toolbar.
7181      * @return {Roo.Element}
7182      */
7183     getEl : function(){
7184         return this.el;  
7185     },
7186     
7187     /**
7188      * Adds a separator
7189      * @return {Roo.Toolbar.Item} The separator item
7190      */
7191     addSeparator : function(){
7192         return this.addItem(new Roo.Toolbar.Separator());
7193     },
7194
7195     /**
7196      * Adds a spacer element
7197      * @return {Roo.Toolbar.Spacer} The spacer item
7198      */
7199     addSpacer : function(){
7200         return this.addItem(new Roo.Toolbar.Spacer());
7201     },
7202
7203     /**
7204      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7205      * @return {Roo.Toolbar.Fill} The fill item
7206      */
7207     addFill : function(){
7208         return this.addItem(new Roo.Toolbar.Fill());
7209     },
7210
7211     /**
7212      * Adds any standard HTML element to the toolbar
7213      * @param {String/HTMLElement/Element} el The element or id of the element to add
7214      * @return {Roo.Toolbar.Item} The element's item
7215      */
7216     addElement : function(el){
7217         return this.addItem(new Roo.Toolbar.Item(el));
7218     },
7219     /**
7220      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7221      * @type Roo.util.MixedCollection  
7222      */
7223     items : false,
7224      
7225     /**
7226      * Adds any Toolbar.Item or subclass
7227      * @param {Roo.Toolbar.Item} item
7228      * @return {Roo.Toolbar.Item} The item
7229      */
7230     addItem : function(item){
7231         var td = this.nextBlock();
7232         item.render(td);
7233         this.items.add(item);
7234         return item;
7235     },
7236     
7237     /**
7238      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7239      * @param {Object/Array} config A button config or array of configs
7240      * @return {Roo.Toolbar.Button/Array}
7241      */
7242     addButton : function(config){
7243         if(config instanceof Array){
7244             var buttons = [];
7245             for(var i = 0, len = config.length; i < len; i++) {
7246                 buttons.push(this.addButton(config[i]));
7247             }
7248             return buttons;
7249         }
7250         var b = config;
7251         if(!(config instanceof Roo.Toolbar.Button)){
7252             b = config.split ?
7253                 new Roo.Toolbar.SplitButton(config) :
7254                 new Roo.Toolbar.Button(config);
7255         }
7256         var td = this.nextBlock();
7257         b.render(td);
7258         this.items.add(b);
7259         return b;
7260     },
7261     
7262     /**
7263      * Adds text to the toolbar
7264      * @param {String} text The text to add
7265      * @return {Roo.Toolbar.Item} The element's item
7266      */
7267     addText : function(text){
7268         return this.addItem(new Roo.Toolbar.TextItem(text));
7269     },
7270     
7271     /**
7272      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7273      * @param {Number} index The index where the item is to be inserted
7274      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7275      * @return {Roo.Toolbar.Button/Item}
7276      */
7277     insertButton : function(index, item){
7278         if(item instanceof Array){
7279             var buttons = [];
7280             for(var i = 0, len = item.length; i < len; i++) {
7281                buttons.push(this.insertButton(index + i, item[i]));
7282             }
7283             return buttons;
7284         }
7285         if (!(item instanceof Roo.Toolbar.Button)){
7286            item = new Roo.Toolbar.Button(item);
7287         }
7288         var td = document.createElement("td");
7289         this.tr.insertBefore(td, this.tr.childNodes[index]);
7290         item.render(td);
7291         this.items.insert(index, item);
7292         return item;
7293     },
7294     
7295     /**
7296      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7297      * @param {Object} config
7298      * @return {Roo.Toolbar.Item} The element's item
7299      */
7300     addDom : function(config, returnEl){
7301         var td = this.nextBlock();
7302         Roo.DomHelper.overwrite(td, config);
7303         var ti = new Roo.Toolbar.Item(td.firstChild);
7304         ti.render(td);
7305         this.items.add(ti);
7306         return ti;
7307     },
7308
7309     /**
7310      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7311      * @type Roo.util.MixedCollection  
7312      */
7313     fields : false,
7314     
7315     /**
7316      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7317      * Note: the field should not have been rendered yet. For a field that has already been
7318      * rendered, use {@link #addElement}.
7319      * @param {Roo.form.Field} field
7320      * @return {Roo.ToolbarItem}
7321      */
7322      
7323       
7324     addField : function(field) {
7325         if (!this.fields) {
7326             var autoId = 0;
7327             this.fields = new Roo.util.MixedCollection(false, function(o){
7328                 return o.id || ("item" + (++autoId));
7329             });
7330
7331         }
7332         
7333         var td = this.nextBlock();
7334         field.render(td);
7335         var ti = new Roo.Toolbar.Item(td.firstChild);
7336         ti.render(td);
7337         this.items.add(ti);
7338         this.fields.add(field);
7339         return ti;
7340     },
7341     /**
7342      * Hide the toolbar
7343      * @method hide
7344      */
7345      
7346       
7347     hide : function()
7348     {
7349         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7350         this.el.child('div').hide();
7351     },
7352     /**
7353      * Show the toolbar
7354      * @method show
7355      */
7356     show : function()
7357     {
7358         this.el.child('div').show();
7359     },
7360       
7361     // private
7362     nextBlock : function(){
7363         var td = document.createElement("td");
7364         this.tr.appendChild(td);
7365         return td;
7366     },
7367
7368     // private
7369     destroy : function(){
7370         if(this.items){ // rendered?
7371             Roo.destroy.apply(Roo, this.items.items);
7372         }
7373         if(this.fields){ // rendered?
7374             Roo.destroy.apply(Roo, this.fields.items);
7375         }
7376         Roo.Element.uncache(this.el, this.tr);
7377     }
7378 };
7379
7380 /**
7381  * @class Roo.Toolbar.Item
7382  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7383  * @constructor
7384  * Creates a new Item
7385  * @param {HTMLElement} el 
7386  */
7387 Roo.Toolbar.Item = function(el){
7388     var cfg = {};
7389     if (typeof (el.xtype) != 'undefined') {
7390         cfg = el;
7391         el = cfg.el;
7392     }
7393     
7394     this.el = Roo.getDom(el);
7395     this.id = Roo.id(this.el);
7396     this.hidden = false;
7397     
7398     this.addEvents({
7399          /**
7400              * @event render
7401              * Fires when the button is rendered
7402              * @param {Button} this
7403              */
7404         'render': true
7405     });
7406     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7407 };
7408 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7409 //Roo.Toolbar.Item.prototype = {
7410     
7411     /**
7412      * Get this item's HTML Element
7413      * @return {HTMLElement}
7414      */
7415     getEl : function(){
7416        return this.el;  
7417     },
7418
7419     // private
7420     render : function(td){
7421         
7422          this.td = td;
7423         td.appendChild(this.el);
7424         
7425         this.fireEvent('render', this);
7426     },
7427     
7428     /**
7429      * Removes and destroys this item.
7430      */
7431     destroy : function(){
7432         this.td.parentNode.removeChild(this.td);
7433     },
7434     
7435     /**
7436      * Shows this item.
7437      */
7438     show: function(){
7439         this.hidden = false;
7440         this.td.style.display = "";
7441     },
7442     
7443     /**
7444      * Hides this item.
7445      */
7446     hide: function(){
7447         this.hidden = true;
7448         this.td.style.display = "none";
7449     },
7450     
7451     /**
7452      * Convenience function for boolean show/hide.
7453      * @param {Boolean} visible true to show/false to hide
7454      */
7455     setVisible: function(visible){
7456         if(visible) {
7457             this.show();
7458         }else{
7459             this.hide();
7460         }
7461     },
7462     
7463     /**
7464      * Try to focus this item.
7465      */
7466     focus : function(){
7467         Roo.fly(this.el).focus();
7468     },
7469     
7470     /**
7471      * Disables this item.
7472      */
7473     disable : function(){
7474         Roo.fly(this.td).addClass("x-item-disabled");
7475         this.disabled = true;
7476         this.el.disabled = true;
7477     },
7478     
7479     /**
7480      * Enables this item.
7481      */
7482     enable : function(){
7483         Roo.fly(this.td).removeClass("x-item-disabled");
7484         this.disabled = false;
7485         this.el.disabled = false;
7486     }
7487 });
7488
7489
7490 /**
7491  * @class Roo.Toolbar.Separator
7492  * @extends Roo.Toolbar.Item
7493  * A simple toolbar separator class
7494  * @constructor
7495  * Creates a new Separator
7496  */
7497 Roo.Toolbar.Separator = function(cfg){
7498     
7499     var s = document.createElement("span");
7500     s.className = "ytb-sep";
7501     if (cfg) {
7502         cfg.el = s;
7503     }
7504     
7505     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7506 };
7507 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7508     enable:Roo.emptyFn,
7509     disable:Roo.emptyFn,
7510     focus:Roo.emptyFn
7511 });
7512
7513 /**
7514  * @class Roo.Toolbar.Spacer
7515  * @extends Roo.Toolbar.Item
7516  * A simple element that adds extra horizontal space to a toolbar.
7517  * @constructor
7518  * Creates a new Spacer
7519  */
7520 Roo.Toolbar.Spacer = function(cfg){
7521     var s = document.createElement("div");
7522     s.className = "ytb-spacer";
7523     if (cfg) {
7524         cfg.el = s;
7525     }
7526     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7527 };
7528 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7529     enable:Roo.emptyFn,
7530     disable:Roo.emptyFn,
7531     focus:Roo.emptyFn
7532 });
7533
7534 /**
7535  * @class Roo.Toolbar.Fill
7536  * @extends Roo.Toolbar.Spacer
7537  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7538  * @constructor
7539  * Creates a new Spacer
7540  */
7541 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7542     // private
7543     render : function(td){
7544         td.style.width = '100%';
7545         Roo.Toolbar.Fill.superclass.render.call(this, td);
7546     }
7547 });
7548
7549 /**
7550  * @class Roo.Toolbar.TextItem
7551  * @extends Roo.Toolbar.Item
7552  * A simple class that renders text directly into a toolbar.
7553  * @constructor
7554  * Creates a new TextItem
7555  * @cfg {string} text 
7556  */
7557 Roo.Toolbar.TextItem = function(cfg){
7558     var  text = cfg || "";
7559     if (typeof(cfg) == 'object') {
7560         text = cfg.text || "";
7561     }  else {
7562         cfg = null;
7563     }
7564     var s = document.createElement("span");
7565     s.className = "ytb-text";
7566     s.innerHTML = text;
7567     if (cfg) {
7568         cfg.el  = s;
7569     }
7570     
7571     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7572 };
7573 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7574     
7575      
7576     enable:Roo.emptyFn,
7577     disable:Roo.emptyFn,
7578     focus:Roo.emptyFn,
7579      /**
7580      * Shows this button
7581      */
7582     show: function(){
7583         this.hidden = false;
7584         this.el.style.display = "";
7585     },
7586     
7587     /**
7588      * Hides this button
7589      */
7590     hide: function(){
7591         this.hidden = true;
7592         this.el.style.display = "none";
7593     }
7594     
7595 });
7596
7597 /**
7598  * @class Roo.Toolbar.Button
7599  * @extends Roo.Button
7600  * A button that renders into a toolbar.
7601  * @constructor
7602  * Creates a new Button
7603  * @param {Object} config A standard {@link Roo.Button} config object
7604  */
7605 Roo.Toolbar.Button = function(config){
7606     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7607 };
7608 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7609 {
7610     
7611     
7612     render : function(td){
7613         this.td = td;
7614         Roo.Toolbar.Button.superclass.render.call(this, td);
7615     },
7616     
7617     /**
7618      * Removes and destroys this button
7619      */
7620     destroy : function(){
7621         Roo.Toolbar.Button.superclass.destroy.call(this);
7622         this.td.parentNode.removeChild(this.td);
7623     },
7624     
7625     /**
7626      * Shows this button
7627      */
7628     show: function(){
7629         this.hidden = false;
7630         this.td.style.display = "";
7631     },
7632     
7633     /**
7634      * Hides this button
7635      */
7636     hide: function(){
7637         this.hidden = true;
7638         this.td.style.display = "none";
7639     },
7640
7641     /**
7642      * Disables this item
7643      */
7644     disable : function(){
7645         Roo.fly(this.td).addClass("x-item-disabled");
7646         this.disabled = true;
7647     },
7648
7649     /**
7650      * Enables this item
7651      */
7652     enable : function(){
7653         Roo.fly(this.td).removeClass("x-item-disabled");
7654         this.disabled = false;
7655     }
7656 });
7657 // backwards compat
7658 Roo.ToolbarButton = Roo.Toolbar.Button;
7659
7660 /**
7661  * @class Roo.Toolbar.SplitButton
7662  * @extends Roo.SplitButton
7663  * A menu button that renders into a toolbar.
7664  * @constructor
7665  * Creates a new SplitButton
7666  * @param {Object} config A standard {@link Roo.SplitButton} config object
7667  */
7668 Roo.Toolbar.SplitButton = function(config){
7669     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7670 };
7671 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7672     render : function(td){
7673         this.td = td;
7674         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7675     },
7676     
7677     /**
7678      * Removes and destroys this button
7679      */
7680     destroy : function(){
7681         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7682         this.td.parentNode.removeChild(this.td);
7683     },
7684     
7685     /**
7686      * Shows this button
7687      */
7688     show: function(){
7689         this.hidden = false;
7690         this.td.style.display = "";
7691     },
7692     
7693     /**
7694      * Hides this button
7695      */
7696     hide: function(){
7697         this.hidden = true;
7698         this.td.style.display = "none";
7699     }
7700 });
7701
7702 // backwards compat
7703 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7704  * Based on:
7705  * Ext JS Library 1.1.1
7706  * Copyright(c) 2006-2007, Ext JS, LLC.
7707  *
7708  * Originally Released Under LGPL - original licence link has changed is not relivant.
7709  *
7710  * Fork - LGPL
7711  * <script type="text/javascript">
7712  */
7713  
7714 /**
7715  * @class Roo.PagingToolbar
7716  * @extends Roo.Toolbar
7717  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7718  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7719  * @constructor
7720  * Create a new PagingToolbar
7721  * @param {Object} config The config object
7722  */
7723 Roo.PagingToolbar = function(el, ds, config)
7724 {
7725     // old args format still supported... - xtype is prefered..
7726     if (typeof(el) == 'object' && el.xtype) {
7727         // created from xtype...
7728         config = el;
7729         ds = el.dataSource;
7730         el = config.container;
7731     }
7732     var items = [];
7733     if (config.items) {
7734         items = config.items;
7735         config.items = [];
7736     }
7737     
7738     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7739     this.ds = ds;
7740     this.cursor = 0;
7741     this.renderButtons(this.el);
7742     this.bind(ds);
7743     
7744     // supprot items array.
7745    
7746     Roo.each(items, function(e) {
7747         this.add(Roo.factory(e));
7748     },this);
7749     
7750 };
7751
7752 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7753    
7754     /**
7755      * @cfg {String/HTMLElement/Element} container
7756      * container The id or element that will contain the toolbar
7757      */
7758     /**
7759      * @cfg {Boolean} displayInfo
7760      * True to display the displayMsg (defaults to false)
7761      */
7762     
7763     
7764     /**
7765      * @cfg {Number} pageSize
7766      * The number of records to display per page (defaults to 20)
7767      */
7768     pageSize: 20,
7769     /**
7770      * @cfg {String} displayMsg
7771      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7772      */
7773     displayMsg : 'Displaying {0} - {1} of {2}',
7774     /**
7775      * @cfg {String} emptyMsg
7776      * The message to display when no records are found (defaults to "No data to display")
7777      */
7778     emptyMsg : 'No data to display',
7779     /**
7780      * Customizable piece of the default paging text (defaults to "Page")
7781      * @type String
7782      */
7783     beforePageText : "Page",
7784     /**
7785      * Customizable piece of the default paging text (defaults to "of %0")
7786      * @type String
7787      */
7788     afterPageText : "of {0}",
7789     /**
7790      * Customizable piece of the default paging text (defaults to "First Page")
7791      * @type String
7792      */
7793     firstText : "First Page",
7794     /**
7795      * Customizable piece of the default paging text (defaults to "Previous Page")
7796      * @type String
7797      */
7798     prevText : "Previous Page",
7799     /**
7800      * Customizable piece of the default paging text (defaults to "Next Page")
7801      * @type String
7802      */
7803     nextText : "Next Page",
7804     /**
7805      * Customizable piece of the default paging text (defaults to "Last Page")
7806      * @type String
7807      */
7808     lastText : "Last Page",
7809     /**
7810      * Customizable piece of the default paging text (defaults to "Refresh")
7811      * @type String
7812      */
7813     refreshText : "Refresh",
7814
7815     // private
7816     renderButtons : function(el){
7817         Roo.PagingToolbar.superclass.render.call(this, el);
7818         this.first = this.addButton({
7819             tooltip: this.firstText,
7820             cls: "x-btn-icon x-grid-page-first",
7821             disabled: true,
7822             handler: this.onClick.createDelegate(this, ["first"])
7823         });
7824         this.prev = this.addButton({
7825             tooltip: this.prevText,
7826             cls: "x-btn-icon x-grid-page-prev",
7827             disabled: true,
7828             handler: this.onClick.createDelegate(this, ["prev"])
7829         });
7830         //this.addSeparator();
7831         this.add(this.beforePageText);
7832         this.field = Roo.get(this.addDom({
7833            tag: "input",
7834            type: "text",
7835            size: "3",
7836            value: "1",
7837            cls: "x-grid-page-number"
7838         }).el);
7839         this.field.on("keydown", this.onPagingKeydown, this);
7840         this.field.on("focus", function(){this.dom.select();});
7841         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7842         this.field.setHeight(18);
7843         //this.addSeparator();
7844         this.next = this.addButton({
7845             tooltip: this.nextText,
7846             cls: "x-btn-icon x-grid-page-next",
7847             disabled: true,
7848             handler: this.onClick.createDelegate(this, ["next"])
7849         });
7850         this.last = this.addButton({
7851             tooltip: this.lastText,
7852             cls: "x-btn-icon x-grid-page-last",
7853             disabled: true,
7854             handler: this.onClick.createDelegate(this, ["last"])
7855         });
7856         //this.addSeparator();
7857         this.loading = this.addButton({
7858             tooltip: this.refreshText,
7859             cls: "x-btn-icon x-grid-loading",
7860             handler: this.onClick.createDelegate(this, ["refresh"])
7861         });
7862
7863         if(this.displayInfo){
7864             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7865         }
7866     },
7867
7868     // private
7869     updateInfo : function(){
7870         if(this.displayEl){
7871             var count = this.ds.getCount();
7872             var msg = count == 0 ?
7873                 this.emptyMsg :
7874                 String.format(
7875                     this.displayMsg,
7876                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7877                 );
7878             this.displayEl.update(msg);
7879         }
7880     },
7881
7882     // private
7883     onLoad : function(ds, r, o){
7884        this.cursor = o.params ? o.params.start : 0;
7885        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7886
7887        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7888        this.field.dom.value = ap;
7889        this.first.setDisabled(ap == 1);
7890        this.prev.setDisabled(ap == 1);
7891        this.next.setDisabled(ap == ps);
7892        this.last.setDisabled(ap == ps);
7893        this.loading.enable();
7894        this.updateInfo();
7895     },
7896
7897     // private
7898     getPageData : function(){
7899         var total = this.ds.getTotalCount();
7900         return {
7901             total : total,
7902             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7903             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7904         };
7905     },
7906
7907     // private
7908     onLoadError : function(){
7909         this.loading.enable();
7910     },
7911
7912     // private
7913     onPagingKeydown : function(e){
7914         var k = e.getKey();
7915         var d = this.getPageData();
7916         if(k == e.RETURN){
7917             var v = this.field.dom.value, pageNum;
7918             if(!v || isNaN(pageNum = parseInt(v, 10))){
7919                 this.field.dom.value = d.activePage;
7920                 return;
7921             }
7922             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7923             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7924             e.stopEvent();
7925         }
7926         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))
7927         {
7928           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7929           this.field.dom.value = pageNum;
7930           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7931           e.stopEvent();
7932         }
7933         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7934         {
7935           var v = this.field.dom.value, pageNum; 
7936           var increment = (e.shiftKey) ? 10 : 1;
7937           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7938             increment *= -1;
7939           }
7940           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7941             this.field.dom.value = d.activePage;
7942             return;
7943           }
7944           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7945           {
7946             this.field.dom.value = parseInt(v, 10) + increment;
7947             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7948             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7949           }
7950           e.stopEvent();
7951         }
7952     },
7953
7954     // private
7955     beforeLoad : function(){
7956         if(this.loading){
7957             this.loading.disable();
7958         }
7959     },
7960     /**
7961      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
7962      * @param {String} which (first|prev|next|last|refresh)  which button to press.
7963      *
7964      */
7965     // private
7966     onClick : function(which){
7967         var ds = this.ds;
7968         switch(which){
7969             case "first":
7970                 ds.load({params:{start: 0, limit: this.pageSize}});
7971             break;
7972             case "prev":
7973                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7974             break;
7975             case "next":
7976                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7977             break;
7978             case "last":
7979                 var total = ds.getTotalCount();
7980                 var extra = total % this.pageSize;
7981                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7982                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7983             break;
7984             case "refresh":
7985                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7986             break;
7987         }
7988     },
7989
7990     /**
7991      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7992      * @param {Roo.data.Store} store The data store to unbind
7993      */
7994     unbind : function(ds){
7995         ds.un("beforeload", this.beforeLoad, this);
7996         ds.un("load", this.onLoad, this);
7997         ds.un("loadexception", this.onLoadError, this);
7998         ds.un("remove", this.updateInfo, this);
7999         ds.un("add", this.updateInfo, this);
8000         this.ds = undefined;
8001     },
8002
8003     /**
8004      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8005      * @param {Roo.data.Store} store The data store to bind
8006      */
8007     bind : function(ds){
8008         ds.on("beforeload", this.beforeLoad, this);
8009         ds.on("load", this.onLoad, this);
8010         ds.on("loadexception", this.onLoadError, this);
8011         ds.on("remove", this.updateInfo, this);
8012         ds.on("add", this.updateInfo, this);
8013         this.ds = ds;
8014     }
8015 });/*
8016  * Based on:
8017  * Ext JS Library 1.1.1
8018  * Copyright(c) 2006-2007, Ext JS, LLC.
8019  *
8020  * Originally Released Under LGPL - original licence link has changed is not relivant.
8021  *
8022  * Fork - LGPL
8023  * <script type="text/javascript">
8024  */
8025
8026 /**
8027  * @class Roo.Resizable
8028  * @extends Roo.util.Observable
8029  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8030  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8031  * 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
8032  * the element will be wrapped for you automatically.</p>
8033  * <p>Here is the list of valid resize handles:</p>
8034  * <pre>
8035 Value   Description
8036 ------  -------------------
8037  'n'     north
8038  's'     south
8039  'e'     east
8040  'w'     west
8041  'nw'    northwest
8042  'sw'    southwest
8043  'se'    southeast
8044  'ne'    northeast
8045  'hd'    horizontal drag
8046  'all'   all
8047 </pre>
8048  * <p>Here's an example showing the creation of a typical Resizable:</p>
8049  * <pre><code>
8050 var resizer = new Roo.Resizable("element-id", {
8051     handles: 'all',
8052     minWidth: 200,
8053     minHeight: 100,
8054     maxWidth: 500,
8055     maxHeight: 400,
8056     pinned: true
8057 });
8058 resizer.on("resize", myHandler);
8059 </code></pre>
8060  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8061  * resizer.east.setDisplayed(false);</p>
8062  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8063  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8064  * resize operation's new size (defaults to [0, 0])
8065  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8066  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8067  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8068  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8069  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8070  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8071  * @cfg {Number} width The width of the element in pixels (defaults to null)
8072  * @cfg {Number} height The height of the element in pixels (defaults to null)
8073  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8074  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8075  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8076  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8077  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8078  * in favor of the handles config option (defaults to false)
8079  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8080  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8081  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8082  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8083  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8084  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8085  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8086  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8087  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8088  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8089  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8090  * @constructor
8091  * Create a new resizable component
8092  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8093  * @param {Object} config configuration options
8094   */
8095 Roo.Resizable = function(el, config)
8096 {
8097     this.el = Roo.get(el);
8098
8099     if(config && config.wrap){
8100         config.resizeChild = this.el;
8101         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8102         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8103         this.el.setStyle("overflow", "hidden");
8104         this.el.setPositioning(config.resizeChild.getPositioning());
8105         config.resizeChild.clearPositioning();
8106         if(!config.width || !config.height){
8107             var csize = config.resizeChild.getSize();
8108             this.el.setSize(csize.width, csize.height);
8109         }
8110         if(config.pinned && !config.adjustments){
8111             config.adjustments = "auto";
8112         }
8113     }
8114
8115     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8116     this.proxy.unselectable();
8117     this.proxy.enableDisplayMode('block');
8118
8119     Roo.apply(this, config);
8120
8121     if(this.pinned){
8122         this.disableTrackOver = true;
8123         this.el.addClass("x-resizable-pinned");
8124     }
8125     // if the element isn't positioned, make it relative
8126     var position = this.el.getStyle("position");
8127     if(position != "absolute" && position != "fixed"){
8128         this.el.setStyle("position", "relative");
8129     }
8130     if(!this.handles){ // no handles passed, must be legacy style
8131         this.handles = 's,e,se';
8132         if(this.multiDirectional){
8133             this.handles += ',n,w';
8134         }
8135     }
8136     if(this.handles == "all"){
8137         this.handles = "n s e w ne nw se sw";
8138     }
8139     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8140     var ps = Roo.Resizable.positions;
8141     for(var i = 0, len = hs.length; i < len; i++){
8142         if(hs[i] && ps[hs[i]]){
8143             var pos = ps[hs[i]];
8144             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8145         }
8146     }
8147     // legacy
8148     this.corner = this.southeast;
8149     
8150     // updateBox = the box can move..
8151     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8152         this.updateBox = true;
8153     }
8154
8155     this.activeHandle = null;
8156
8157     if(this.resizeChild){
8158         if(typeof this.resizeChild == "boolean"){
8159             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8160         }else{
8161             this.resizeChild = Roo.get(this.resizeChild, true);
8162         }
8163     }
8164     
8165     if(this.adjustments == "auto"){
8166         var rc = this.resizeChild;
8167         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8168         if(rc && (hw || hn)){
8169             rc.position("relative");
8170             rc.setLeft(hw ? hw.el.getWidth() : 0);
8171             rc.setTop(hn ? hn.el.getHeight() : 0);
8172         }
8173         this.adjustments = [
8174             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8175             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8176         ];
8177     }
8178
8179     if(this.draggable){
8180         this.dd = this.dynamic ?
8181             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8182         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8183     }
8184
8185     // public events
8186     this.addEvents({
8187         /**
8188          * @event beforeresize
8189          * Fired before resize is allowed. Set enabled to false to cancel resize.
8190          * @param {Roo.Resizable} this
8191          * @param {Roo.EventObject} e The mousedown event
8192          */
8193         "beforeresize" : true,
8194         /**
8195          * @event resizing
8196          * Fired a resizing.
8197          * @param {Roo.Resizable} this
8198          * @param {Number} x The new x position
8199          * @param {Number} y The new y position
8200          * @param {Number} w The new w width
8201          * @param {Number} h The new h hight
8202          * @param {Roo.EventObject} e The mouseup event
8203          */
8204         "resizing" : true,
8205         /**
8206          * @event resize
8207          * Fired after a resize.
8208          * @param {Roo.Resizable} this
8209          * @param {Number} width The new width
8210          * @param {Number} height The new height
8211          * @param {Roo.EventObject} e The mouseup event
8212          */
8213         "resize" : true
8214     });
8215
8216     if(this.width !== null && this.height !== null){
8217         this.resizeTo(this.width, this.height);
8218     }else{
8219         this.updateChildSize();
8220     }
8221     if(Roo.isIE){
8222         this.el.dom.style.zoom = 1;
8223     }
8224     Roo.Resizable.superclass.constructor.call(this);
8225 };
8226
8227 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8228         resizeChild : false,
8229         adjustments : [0, 0],
8230         minWidth : 5,
8231         minHeight : 5,
8232         maxWidth : 10000,
8233         maxHeight : 10000,
8234         enabled : true,
8235         animate : false,
8236         duration : .35,
8237         dynamic : false,
8238         handles : false,
8239         multiDirectional : false,
8240         disableTrackOver : false,
8241         easing : 'easeOutStrong',
8242         widthIncrement : 0,
8243         heightIncrement : 0,
8244         pinned : false,
8245         width : null,
8246         height : null,
8247         preserveRatio : false,
8248         transparent: false,
8249         minX: 0,
8250         minY: 0,
8251         draggable: false,
8252
8253         /**
8254          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8255          */
8256         constrainTo: undefined,
8257         /**
8258          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8259          */
8260         resizeRegion: undefined,
8261
8262
8263     /**
8264      * Perform a manual resize
8265      * @param {Number} width
8266      * @param {Number} height
8267      */
8268     resizeTo : function(width, height){
8269         this.el.setSize(width, height);
8270         this.updateChildSize();
8271         this.fireEvent("resize", this, width, height, null);
8272     },
8273
8274     // private
8275     startSizing : function(e, handle){
8276         this.fireEvent("beforeresize", this, e);
8277         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8278
8279             if(!this.overlay){
8280                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8281                 this.overlay.unselectable();
8282                 this.overlay.enableDisplayMode("block");
8283                 this.overlay.on("mousemove", this.onMouseMove, this);
8284                 this.overlay.on("mouseup", this.onMouseUp, this);
8285             }
8286             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8287
8288             this.resizing = true;
8289             this.startBox = this.el.getBox();
8290             this.startPoint = e.getXY();
8291             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8292                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8293
8294             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8295             this.overlay.show();
8296
8297             if(this.constrainTo) {
8298                 var ct = Roo.get(this.constrainTo);
8299                 this.resizeRegion = ct.getRegion().adjust(
8300                     ct.getFrameWidth('t'),
8301                     ct.getFrameWidth('l'),
8302                     -ct.getFrameWidth('b'),
8303                     -ct.getFrameWidth('r')
8304                 );
8305             }
8306
8307             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8308             this.proxy.show();
8309             this.proxy.setBox(this.startBox);
8310             if(!this.dynamic){
8311                 this.proxy.setStyle('visibility', 'visible');
8312             }
8313         }
8314     },
8315
8316     // private
8317     onMouseDown : function(handle, e){
8318         if(this.enabled){
8319             e.stopEvent();
8320             this.activeHandle = handle;
8321             this.startSizing(e, handle);
8322         }
8323     },
8324
8325     // private
8326     onMouseUp : function(e){
8327         var size = this.resizeElement();
8328         this.resizing = false;
8329         this.handleOut();
8330         this.overlay.hide();
8331         this.proxy.hide();
8332         this.fireEvent("resize", this, size.width, size.height, e);
8333     },
8334
8335     // private
8336     updateChildSize : function(){
8337         
8338         if(this.resizeChild){
8339             var el = this.el;
8340             var child = this.resizeChild;
8341             var adj = this.adjustments;
8342             if(el.dom.offsetWidth){
8343                 var b = el.getSize(true);
8344                 child.setSize(b.width+adj[0], b.height+adj[1]);
8345             }
8346             // Second call here for IE
8347             // The first call enables instant resizing and
8348             // the second call corrects scroll bars if they
8349             // exist
8350             if(Roo.isIE){
8351                 setTimeout(function(){
8352                     if(el.dom.offsetWidth){
8353                         var b = el.getSize(true);
8354                         child.setSize(b.width+adj[0], b.height+adj[1]);
8355                     }
8356                 }, 10);
8357             }
8358         }
8359     },
8360
8361     // private
8362     snap : function(value, inc, min){
8363         if(!inc || !value) {
8364             return value;
8365         }
8366         var newValue = value;
8367         var m = value % inc;
8368         if(m > 0){
8369             if(m > (inc/2)){
8370                 newValue = value + (inc-m);
8371             }else{
8372                 newValue = value - m;
8373             }
8374         }
8375         return Math.max(min, newValue);
8376     },
8377
8378     // private
8379     resizeElement : function(){
8380         var box = this.proxy.getBox();
8381         if(this.updateBox){
8382             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8383         }else{
8384             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8385         }
8386         this.updateChildSize();
8387         if(!this.dynamic){
8388             this.proxy.hide();
8389         }
8390         return box;
8391     },
8392
8393     // private
8394     constrain : function(v, diff, m, mx){
8395         if(v - diff < m){
8396             diff = v - m;
8397         }else if(v - diff > mx){
8398             diff = mx - v;
8399         }
8400         return diff;
8401     },
8402
8403     // private
8404     onMouseMove : function(e){
8405         
8406         if(this.enabled){
8407             try{// try catch so if something goes wrong the user doesn't get hung
8408
8409             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8410                 return;
8411             }
8412
8413             //var curXY = this.startPoint;
8414             var curSize = this.curSize || this.startBox;
8415             var x = this.startBox.x, y = this.startBox.y;
8416             var ox = x, oy = y;
8417             var w = curSize.width, h = curSize.height;
8418             var ow = w, oh = h;
8419             var mw = this.minWidth, mh = this.minHeight;
8420             var mxw = this.maxWidth, mxh = this.maxHeight;
8421             var wi = this.widthIncrement;
8422             var hi = this.heightIncrement;
8423
8424             var eventXY = e.getXY();
8425             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8426             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8427
8428             var pos = this.activeHandle.position;
8429
8430             switch(pos){
8431                 case "east":
8432                     w += diffX;
8433                     w = Math.min(Math.max(mw, w), mxw);
8434                     break;
8435              
8436                 case "south":
8437                     h += diffY;
8438                     h = Math.min(Math.max(mh, h), mxh);
8439                     break;
8440                 case "southeast":
8441                     w += diffX;
8442                     h += diffY;
8443                     w = Math.min(Math.max(mw, w), mxw);
8444                     h = Math.min(Math.max(mh, h), mxh);
8445                     break;
8446                 case "north":
8447                     diffY = this.constrain(h, diffY, mh, mxh);
8448                     y += diffY;
8449                     h -= diffY;
8450                     break;
8451                 case "hdrag":
8452                     
8453                     if (wi) {
8454                         var adiffX = Math.abs(diffX);
8455                         var sub = (adiffX % wi); // how much 
8456                         if (sub > (wi/2)) { // far enough to snap
8457                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8458                         } else {
8459                             // remove difference.. 
8460                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8461                         }
8462                     }
8463                     x += diffX;
8464                     x = Math.max(this.minX, x);
8465                     break;
8466                 case "west":
8467                     diffX = this.constrain(w, diffX, mw, mxw);
8468                     x += diffX;
8469                     w -= diffX;
8470                     break;
8471                 case "northeast":
8472                     w += diffX;
8473                     w = Math.min(Math.max(mw, w), mxw);
8474                     diffY = this.constrain(h, diffY, mh, mxh);
8475                     y += diffY;
8476                     h -= diffY;
8477                     break;
8478                 case "northwest":
8479                     diffX = this.constrain(w, diffX, mw, mxw);
8480                     diffY = this.constrain(h, diffY, mh, mxh);
8481                     y += diffY;
8482                     h -= diffY;
8483                     x += diffX;
8484                     w -= diffX;
8485                     break;
8486                case "southwest":
8487                     diffX = this.constrain(w, diffX, mw, mxw);
8488                     h += diffY;
8489                     h = Math.min(Math.max(mh, h), mxh);
8490                     x += diffX;
8491                     w -= diffX;
8492                     break;
8493             }
8494
8495             var sw = this.snap(w, wi, mw);
8496             var sh = this.snap(h, hi, mh);
8497             if(sw != w || sh != h){
8498                 switch(pos){
8499                     case "northeast":
8500                         y -= sh - h;
8501                     break;
8502                     case "north":
8503                         y -= sh - h;
8504                         break;
8505                     case "southwest":
8506                         x -= sw - w;
8507                     break;
8508                     case "west":
8509                         x -= sw - w;
8510                         break;
8511                     case "northwest":
8512                         x -= sw - w;
8513                         y -= sh - h;
8514                     break;
8515                 }
8516                 w = sw;
8517                 h = sh;
8518             }
8519
8520             if(this.preserveRatio){
8521                 switch(pos){
8522                     case "southeast":
8523                     case "east":
8524                         h = oh * (w/ow);
8525                         h = Math.min(Math.max(mh, h), mxh);
8526                         w = ow * (h/oh);
8527                        break;
8528                     case "south":
8529                         w = ow * (h/oh);
8530                         w = Math.min(Math.max(mw, w), mxw);
8531                         h = oh * (w/ow);
8532                         break;
8533                     case "northeast":
8534                         w = ow * (h/oh);
8535                         w = Math.min(Math.max(mw, w), mxw);
8536                         h = oh * (w/ow);
8537                     break;
8538                     case "north":
8539                         var tw = w;
8540                         w = ow * (h/oh);
8541                         w = Math.min(Math.max(mw, w), mxw);
8542                         h = oh * (w/ow);
8543                         x += (tw - w) / 2;
8544                         break;
8545                     case "southwest":
8546                         h = oh * (w/ow);
8547                         h = Math.min(Math.max(mh, h), mxh);
8548                         var tw = w;
8549                         w = ow * (h/oh);
8550                         x += tw - w;
8551                         break;
8552                     case "west":
8553                         var th = h;
8554                         h = oh * (w/ow);
8555                         h = Math.min(Math.max(mh, h), mxh);
8556                         y += (th - h) / 2;
8557                         var tw = w;
8558                         w = ow * (h/oh);
8559                         x += tw - w;
8560                        break;
8561                     case "northwest":
8562                         var tw = w;
8563                         var th = h;
8564                         h = oh * (w/ow);
8565                         h = Math.min(Math.max(mh, h), mxh);
8566                         w = ow * (h/oh);
8567                         y += th - h;
8568                         x += tw - w;
8569                        break;
8570
8571                 }
8572             }
8573             if (pos == 'hdrag') {
8574                 w = ow;
8575             }
8576             this.proxy.setBounds(x, y, w, h);
8577             if(this.dynamic){
8578                 this.resizeElement();
8579             }
8580             }catch(e){}
8581         }
8582         this.fireEvent("resizing", this, x, y, w, h, e);
8583     },
8584
8585     // private
8586     handleOver : function(){
8587         if(this.enabled){
8588             this.el.addClass("x-resizable-over");
8589         }
8590     },
8591
8592     // private
8593     handleOut : function(){
8594         if(!this.resizing){
8595             this.el.removeClass("x-resizable-over");
8596         }
8597     },
8598
8599     /**
8600      * Returns the element this component is bound to.
8601      * @return {Roo.Element}
8602      */
8603     getEl : function(){
8604         return this.el;
8605     },
8606
8607     /**
8608      * Returns the resizeChild element (or null).
8609      * @return {Roo.Element}
8610      */
8611     getResizeChild : function(){
8612         return this.resizeChild;
8613     },
8614     groupHandler : function()
8615     {
8616         
8617     },
8618     /**
8619      * Destroys this resizable. If the element was wrapped and
8620      * removeEl is not true then the element remains.
8621      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8622      */
8623     destroy : function(removeEl){
8624         this.proxy.remove();
8625         if(this.overlay){
8626             this.overlay.removeAllListeners();
8627             this.overlay.remove();
8628         }
8629         var ps = Roo.Resizable.positions;
8630         for(var k in ps){
8631             if(typeof ps[k] != "function" && this[ps[k]]){
8632                 var h = this[ps[k]];
8633                 h.el.removeAllListeners();
8634                 h.el.remove();
8635             }
8636         }
8637         if(removeEl){
8638             this.el.update("");
8639             this.el.remove();
8640         }
8641     }
8642 });
8643
8644 // private
8645 // hash to map config positions to true positions
8646 Roo.Resizable.positions = {
8647     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8648     hd: "hdrag"
8649 };
8650
8651 // private
8652 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8653     if(!this.tpl){
8654         // only initialize the template if resizable is used
8655         var tpl = Roo.DomHelper.createTemplate(
8656             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8657         );
8658         tpl.compile();
8659         Roo.Resizable.Handle.prototype.tpl = tpl;
8660     }
8661     this.position = pos;
8662     this.rz = rz;
8663     // show north drag fro topdra
8664     var handlepos = pos == 'hdrag' ? 'north' : pos;
8665     
8666     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8667     if (pos == 'hdrag') {
8668         this.el.setStyle('cursor', 'pointer');
8669     }
8670     this.el.unselectable();
8671     if(transparent){
8672         this.el.setOpacity(0);
8673     }
8674     this.el.on("mousedown", this.onMouseDown, this);
8675     if(!disableTrackOver){
8676         this.el.on("mouseover", this.onMouseOver, this);
8677         this.el.on("mouseout", this.onMouseOut, this);
8678     }
8679 };
8680
8681 // private
8682 Roo.Resizable.Handle.prototype = {
8683     afterResize : function(rz){
8684         Roo.log('after?');
8685         // do nothing
8686     },
8687     // private
8688     onMouseDown : function(e){
8689         this.rz.onMouseDown(this, e);
8690     },
8691     // private
8692     onMouseOver : function(e){
8693         this.rz.handleOver(this, e);
8694     },
8695     // private
8696     onMouseOut : function(e){
8697         this.rz.handleOut(this, e);
8698     }
8699 };/*
8700  * Based on:
8701  * Ext JS Library 1.1.1
8702  * Copyright(c) 2006-2007, Ext JS, LLC.
8703  *
8704  * Originally Released Under LGPL - original licence link has changed is not relivant.
8705  *
8706  * Fork - LGPL
8707  * <script type="text/javascript">
8708  */
8709
8710 /**
8711  * @class Roo.Editor
8712  * @extends Roo.Component
8713  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8714  * @constructor
8715  * Create a new Editor
8716  * @param {Roo.form.Field} field The Field object (or descendant)
8717  * @param {Object} config The config object
8718  */
8719 Roo.Editor = function(field, config){
8720     Roo.Editor.superclass.constructor.call(this, config);
8721     this.field = field;
8722     this.addEvents({
8723         /**
8724              * @event beforestartedit
8725              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8726              * false from the handler of this event.
8727              * @param {Editor} this
8728              * @param {Roo.Element} boundEl The underlying element bound to this editor
8729              * @param {Mixed} value The field value being set
8730              */
8731         "beforestartedit" : true,
8732         /**
8733              * @event startedit
8734              * Fires when this editor is displayed
8735              * @param {Roo.Element} boundEl The underlying element bound to this editor
8736              * @param {Mixed} value The starting field value
8737              */
8738         "startedit" : true,
8739         /**
8740              * @event beforecomplete
8741              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8742              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8743              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8744              * event will not fire since no edit actually occurred.
8745              * @param {Editor} this
8746              * @param {Mixed} value The current field value
8747              * @param {Mixed} startValue The original field value
8748              */
8749         "beforecomplete" : true,
8750         /**
8751              * @event complete
8752              * Fires after editing is complete and any changed value has been written to the underlying field.
8753              * @param {Editor} this
8754              * @param {Mixed} value The current field value
8755              * @param {Mixed} startValue The original field value
8756              */
8757         "complete" : true,
8758         /**
8759          * @event specialkey
8760          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8761          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8762          * @param {Roo.form.Field} this
8763          * @param {Roo.EventObject} e The event object
8764          */
8765         "specialkey" : true
8766     });
8767 };
8768
8769 Roo.extend(Roo.Editor, Roo.Component, {
8770     /**
8771      * @cfg {Boolean/String} autosize
8772      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8773      * or "height" to adopt the height only (defaults to false)
8774      */
8775     /**
8776      * @cfg {Boolean} revertInvalid
8777      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8778      * validation fails (defaults to true)
8779      */
8780     /**
8781      * @cfg {Boolean} ignoreNoChange
8782      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8783      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8784      * will never be ignored.
8785      */
8786     /**
8787      * @cfg {Boolean} hideEl
8788      * False to keep the bound element visible while the editor is displayed (defaults to true)
8789      */
8790     /**
8791      * @cfg {Mixed} value
8792      * The data value of the underlying field (defaults to "")
8793      */
8794     value : "",
8795     /**
8796      * @cfg {String} alignment
8797      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8798      */
8799     alignment: "c-c?",
8800     /**
8801      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8802      * for bottom-right shadow (defaults to "frame")
8803      */
8804     shadow : "frame",
8805     /**
8806      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8807      */
8808     constrain : false,
8809     /**
8810      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8811      */
8812     completeOnEnter : false,
8813     /**
8814      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8815      */
8816     cancelOnEsc : false,
8817     /**
8818      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8819      */
8820     updateEl : false,
8821
8822     // private
8823     onRender : function(ct, position){
8824         this.el = new Roo.Layer({
8825             shadow: this.shadow,
8826             cls: "x-editor",
8827             parentEl : ct,
8828             shim : this.shim,
8829             shadowOffset:4,
8830             id: this.id,
8831             constrain: this.constrain
8832         });
8833         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8834         if(this.field.msgTarget != 'title'){
8835             this.field.msgTarget = 'qtip';
8836         }
8837         this.field.render(this.el);
8838         if(Roo.isGecko){
8839             this.field.el.dom.setAttribute('autocomplete', 'off');
8840         }
8841         this.field.on("specialkey", this.onSpecialKey, this);
8842         if(this.swallowKeys){
8843             this.field.el.swallowEvent(['keydown','keypress']);
8844         }
8845         this.field.show();
8846         this.field.on("blur", this.onBlur, this);
8847         if(this.field.grow){
8848             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8849         }
8850     },
8851
8852     onSpecialKey : function(field, e)
8853     {
8854         //Roo.log('editor onSpecialKey');
8855         if(this.completeOnEnter && e.getKey() == e.ENTER){
8856             e.stopEvent();
8857             this.completeEdit();
8858             return;
8859         }
8860         // do not fire special key otherwise it might hide close the editor...
8861         if(e.getKey() == e.ENTER){    
8862             return;
8863         }
8864         if(this.cancelOnEsc && e.getKey() == e.ESC){
8865             this.cancelEdit();
8866             return;
8867         } 
8868         this.fireEvent('specialkey', field, e);
8869     
8870     },
8871
8872     /**
8873      * Starts the editing process and shows the editor.
8874      * @param {String/HTMLElement/Element} el The element to edit
8875      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8876       * to the innerHTML of el.
8877      */
8878     startEdit : function(el, value){
8879         if(this.editing){
8880             this.completeEdit();
8881         }
8882         this.boundEl = Roo.get(el);
8883         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8884         if(!this.rendered){
8885             this.render(this.parentEl || document.body);
8886         }
8887         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8888             return;
8889         }
8890         this.startValue = v;
8891         this.field.setValue(v);
8892         if(this.autoSize){
8893             var sz = this.boundEl.getSize();
8894             switch(this.autoSize){
8895                 case "width":
8896                 this.setSize(sz.width,  "");
8897                 break;
8898                 case "height":
8899                 this.setSize("",  sz.height);
8900                 break;
8901                 default:
8902                 this.setSize(sz.width,  sz.height);
8903             }
8904         }
8905         this.el.alignTo(this.boundEl, this.alignment);
8906         this.editing = true;
8907         if(Roo.QuickTips){
8908             Roo.QuickTips.disable();
8909         }
8910         this.show();
8911     },
8912
8913     /**
8914      * Sets the height and width of this editor.
8915      * @param {Number} width The new width
8916      * @param {Number} height The new height
8917      */
8918     setSize : function(w, h){
8919         this.field.setSize(w, h);
8920         if(this.el){
8921             this.el.sync();
8922         }
8923     },
8924
8925     /**
8926      * Realigns the editor to the bound field based on the current alignment config value.
8927      */
8928     realign : function(){
8929         this.el.alignTo(this.boundEl, this.alignment);
8930     },
8931
8932     /**
8933      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8934      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8935      */
8936     completeEdit : function(remainVisible){
8937         if(!this.editing){
8938             return;
8939         }
8940         var v = this.getValue();
8941         if(this.revertInvalid !== false && !this.field.isValid()){
8942             v = this.startValue;
8943             this.cancelEdit(true);
8944         }
8945         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8946             this.editing = false;
8947             this.hide();
8948             return;
8949         }
8950         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8951             this.editing = false;
8952             if(this.updateEl && this.boundEl){
8953                 this.boundEl.update(v);
8954             }
8955             if(remainVisible !== true){
8956                 this.hide();
8957             }
8958             this.fireEvent("complete", this, v, this.startValue);
8959         }
8960     },
8961
8962     // private
8963     onShow : function(){
8964         this.el.show();
8965         if(this.hideEl !== false){
8966             this.boundEl.hide();
8967         }
8968         this.field.show();
8969         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8970             this.fixIEFocus = true;
8971             this.deferredFocus.defer(50, this);
8972         }else{
8973             this.field.focus();
8974         }
8975         this.fireEvent("startedit", this.boundEl, this.startValue);
8976     },
8977
8978     deferredFocus : function(){
8979         if(this.editing){
8980             this.field.focus();
8981         }
8982     },
8983
8984     /**
8985      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8986      * reverted to the original starting value.
8987      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8988      * cancel (defaults to false)
8989      */
8990     cancelEdit : function(remainVisible){
8991         if(this.editing){
8992             this.setValue(this.startValue);
8993             if(remainVisible !== true){
8994                 this.hide();
8995             }
8996         }
8997     },
8998
8999     // private
9000     onBlur : function(){
9001         if(this.allowBlur !== true && this.editing){
9002             this.completeEdit();
9003         }
9004     },
9005
9006     // private
9007     onHide : function(){
9008         if(this.editing){
9009             this.completeEdit();
9010             return;
9011         }
9012         this.field.blur();
9013         if(this.field.collapse){
9014             this.field.collapse();
9015         }
9016         this.el.hide();
9017         if(this.hideEl !== false){
9018             this.boundEl.show();
9019         }
9020         if(Roo.QuickTips){
9021             Roo.QuickTips.enable();
9022         }
9023     },
9024
9025     /**
9026      * Sets the data value of the editor
9027      * @param {Mixed} value Any valid value supported by the underlying field
9028      */
9029     setValue : function(v){
9030         this.field.setValue(v);
9031     },
9032
9033     /**
9034      * Gets the data value of the editor
9035      * @return {Mixed} The data value
9036      */
9037     getValue : function(){
9038         return this.field.getValue();
9039     }
9040 });/*
9041  * Based on:
9042  * Ext JS Library 1.1.1
9043  * Copyright(c) 2006-2007, Ext JS, LLC.
9044  *
9045  * Originally Released Under LGPL - original licence link has changed is not relivant.
9046  *
9047  * Fork - LGPL
9048  * <script type="text/javascript">
9049  */
9050  
9051 /**
9052  * @class Roo.BasicDialog
9053  * @extends Roo.util.Observable
9054  * @parent none builder
9055  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9056  * <pre><code>
9057 var dlg = new Roo.BasicDialog("my-dlg", {
9058     height: 200,
9059     width: 300,
9060     minHeight: 100,
9061     minWidth: 150,
9062     modal: true,
9063     proxyDrag: true,
9064     shadow: true
9065 });
9066 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9067 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9068 dlg.addButton('Cancel', dlg.hide, dlg);
9069 dlg.show();
9070 </code></pre>
9071   <b>A Dialog should always be a direct child of the body element.</b>
9072  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9073  * @cfg {String} title Default text to display in the title bar (defaults to null)
9074  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9075  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9076  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9077  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9078  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9079  * (defaults to null with no animation)
9080  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9081  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9082  * property for valid values (defaults to 'all')
9083  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9084  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9085  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9086  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9087  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9088  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9089  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9090  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9091  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9092  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9093  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9094  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9095  * draggable = true (defaults to false)
9096  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9097  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9098  * shadow (defaults to false)
9099  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9100  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9101  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9102  * @cfg {Array} buttons Array of buttons
9103  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9104  * @constructor
9105  * Create a new BasicDialog.
9106  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9107  * @param {Object} config Configuration options
9108  */
9109 Roo.BasicDialog = function(el, config){
9110     this.el = Roo.get(el);
9111     var dh = Roo.DomHelper;
9112     if(!this.el && config && config.autoCreate){
9113         if(typeof config.autoCreate == "object"){
9114             if(!config.autoCreate.id){
9115                 config.autoCreate.id = el;
9116             }
9117             this.el = dh.append(document.body,
9118                         config.autoCreate, true);
9119         }else{
9120             this.el = dh.append(document.body,
9121                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9122         }
9123     }
9124     el = this.el;
9125     el.setDisplayed(true);
9126     el.hide = this.hideAction;
9127     this.id = el.id;
9128     el.addClass("x-dlg");
9129
9130     Roo.apply(this, config);
9131
9132     this.proxy = el.createProxy("x-dlg-proxy");
9133     this.proxy.hide = this.hideAction;
9134     this.proxy.setOpacity(.5);
9135     this.proxy.hide();
9136
9137     if(config.width){
9138         el.setWidth(config.width);
9139     }
9140     if(config.height){
9141         el.setHeight(config.height);
9142     }
9143     this.size = el.getSize();
9144     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9145         this.xy = [config.x,config.y];
9146     }else{
9147         this.xy = el.getCenterXY(true);
9148     }
9149     /** The header element @type Roo.Element */
9150     this.header = el.child("> .x-dlg-hd");
9151     /** The body element @type Roo.Element */
9152     this.body = el.child("> .x-dlg-bd");
9153     /** The footer element @type Roo.Element */
9154     this.footer = el.child("> .x-dlg-ft");
9155
9156     if(!this.header){
9157         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9158     }
9159     if(!this.body){
9160         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9161     }
9162
9163     this.header.unselectable();
9164     if(this.title){
9165         this.header.update(this.title);
9166     }
9167     // this element allows the dialog to be focused for keyboard event
9168     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9169     this.focusEl.swallowEvent("click", true);
9170
9171     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9172
9173     // wrap the body and footer for special rendering
9174     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9175     if(this.footer){
9176         this.bwrap.dom.appendChild(this.footer.dom);
9177     }
9178
9179     this.bg = this.el.createChild({
9180         tag: "div", cls:"x-dlg-bg",
9181         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9182     });
9183     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9184
9185
9186     if(this.autoScroll !== false && !this.autoTabs){
9187         this.body.setStyle("overflow", "auto");
9188     }
9189
9190     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9191
9192     if(this.closable !== false){
9193         this.el.addClass("x-dlg-closable");
9194         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9195         this.close.on("click", this.closeClick, this);
9196         this.close.addClassOnOver("x-dlg-close-over");
9197     }
9198     if(this.collapsible !== false){
9199         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9200         this.collapseBtn.on("click", this.collapseClick, this);
9201         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9202         this.header.on("dblclick", this.collapseClick, this);
9203     }
9204     if(this.resizable !== false){
9205         this.el.addClass("x-dlg-resizable");
9206         this.resizer = new Roo.Resizable(el, {
9207             minWidth: this.minWidth || 80,
9208             minHeight:this.minHeight || 80,
9209             handles: this.resizeHandles || "all",
9210             pinned: true
9211         });
9212         this.resizer.on("beforeresize", this.beforeResize, this);
9213         this.resizer.on("resize", this.onResize, this);
9214     }
9215     if(this.draggable !== false){
9216         el.addClass("x-dlg-draggable");
9217         if (!this.proxyDrag) {
9218             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9219         }
9220         else {
9221             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9222         }
9223         dd.setHandleElId(this.header.id);
9224         dd.endDrag = this.endMove.createDelegate(this);
9225         dd.startDrag = this.startMove.createDelegate(this);
9226         dd.onDrag = this.onDrag.createDelegate(this);
9227         dd.scroll = false;
9228         this.dd = dd;
9229     }
9230     if(this.modal){
9231         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9232         this.mask.enableDisplayMode("block");
9233         this.mask.hide();
9234         this.el.addClass("x-dlg-modal");
9235     }
9236     if(this.shadow){
9237         this.shadow = new Roo.Shadow({
9238             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9239             offset : this.shadowOffset
9240         });
9241     }else{
9242         this.shadowOffset = 0;
9243     }
9244     if(Roo.useShims && this.shim !== false){
9245         this.shim = this.el.createShim();
9246         this.shim.hide = this.hideAction;
9247         this.shim.hide();
9248     }else{
9249         this.shim = false;
9250     }
9251     if(this.autoTabs){
9252         this.initTabs();
9253     }
9254     if (this.buttons) { 
9255         var bts= this.buttons;
9256         this.buttons = [];
9257         Roo.each(bts, function(b) {
9258             this.addButton(b);
9259         }, this);
9260     }
9261     
9262     
9263     this.addEvents({
9264         /**
9265          * @event keydown
9266          * Fires when a key is pressed
9267          * @param {Roo.BasicDialog} this
9268          * @param {Roo.EventObject} e
9269          */
9270         "keydown" : true,
9271         /**
9272          * @event move
9273          * Fires when this dialog is moved by the user.
9274          * @param {Roo.BasicDialog} this
9275          * @param {Number} x The new page X
9276          * @param {Number} y The new page Y
9277          */
9278         "move" : true,
9279         /**
9280          * @event resize
9281          * Fires when this dialog is resized by the user.
9282          * @param {Roo.BasicDialog} this
9283          * @param {Number} width The new width
9284          * @param {Number} height The new height
9285          */
9286         "resize" : true,
9287         /**
9288          * @event beforehide
9289          * Fires before this dialog is hidden.
9290          * @param {Roo.BasicDialog} this
9291          */
9292         "beforehide" : true,
9293         /**
9294          * @event hide
9295          * Fires when this dialog is hidden.
9296          * @param {Roo.BasicDialog} this
9297          */
9298         "hide" : true,
9299         /**
9300          * @event beforeshow
9301          * Fires before this dialog is shown.
9302          * @param {Roo.BasicDialog} this
9303          */
9304         "beforeshow" : true,
9305         /**
9306          * @event show
9307          * Fires when this dialog is shown.
9308          * @param {Roo.BasicDialog} this
9309          */
9310         "show" : true
9311     });
9312     el.on("keydown", this.onKeyDown, this);
9313     el.on("mousedown", this.toFront, this);
9314     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9315     this.el.hide();
9316     Roo.DialogManager.register(this);
9317     Roo.BasicDialog.superclass.constructor.call(this);
9318 };
9319
9320 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9321     shadowOffset: Roo.isIE ? 6 : 5,
9322     minHeight: 80,
9323     minWidth: 200,
9324     minButtonWidth: 75,
9325     defaultButton: null,
9326     buttonAlign: "right",
9327     tabTag: 'div',
9328     firstShow: true,
9329
9330     /**
9331      * Sets the dialog title text
9332      * @param {String} text The title text to display
9333      * @return {Roo.BasicDialog} this
9334      */
9335     setTitle : function(text){
9336         this.header.update(text);
9337         return this;
9338     },
9339
9340     // private
9341     closeClick : function(){
9342         this.hide();
9343     },
9344
9345     // private
9346     collapseClick : function(){
9347         this[this.collapsed ? "expand" : "collapse"]();
9348     },
9349
9350     /**
9351      * Collapses the dialog to its minimized state (only the title bar is visible).
9352      * Equivalent to the user clicking the collapse dialog button.
9353      */
9354     collapse : function(){
9355         if(!this.collapsed){
9356             this.collapsed = true;
9357             this.el.addClass("x-dlg-collapsed");
9358             this.restoreHeight = this.el.getHeight();
9359             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9360         }
9361     },
9362
9363     /**
9364      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9365      * clicking the expand dialog button.
9366      */
9367     expand : function(){
9368         if(this.collapsed){
9369             this.collapsed = false;
9370             this.el.removeClass("x-dlg-collapsed");
9371             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9372         }
9373     },
9374
9375     /**
9376      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9377      * @return {Roo.TabPanel} The tabs component
9378      */
9379     initTabs : function(){
9380         var tabs = this.getTabs();
9381         while(tabs.getTab(0)){
9382             tabs.removeTab(0);
9383         }
9384         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9385             var dom = el.dom;
9386             tabs.addTab(Roo.id(dom), dom.title);
9387             dom.title = "";
9388         });
9389         tabs.activate(0);
9390         return tabs;
9391     },
9392
9393     // private
9394     beforeResize : function(){
9395         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9396     },
9397
9398     // private
9399     onResize : function(){
9400         this.refreshSize();
9401         this.syncBodyHeight();
9402         this.adjustAssets();
9403         this.focus();
9404         this.fireEvent("resize", this, this.size.width, this.size.height);
9405     },
9406
9407     // private
9408     onKeyDown : function(e){
9409         if(this.isVisible()){
9410             this.fireEvent("keydown", this, e);
9411         }
9412     },
9413
9414     /**
9415      * Resizes the dialog.
9416      * @param {Number} width
9417      * @param {Number} height
9418      * @return {Roo.BasicDialog} this
9419      */
9420     resizeTo : function(width, height){
9421         this.el.setSize(width, height);
9422         this.size = {width: width, height: height};
9423         this.syncBodyHeight();
9424         if(this.fixedcenter){
9425             this.center();
9426         }
9427         if(this.isVisible()){
9428             this.constrainXY();
9429             this.adjustAssets();
9430         }
9431         this.fireEvent("resize", this, width, height);
9432         return this;
9433     },
9434
9435
9436     /**
9437      * Resizes the dialog to fit the specified content size.
9438      * @param {Number} width
9439      * @param {Number} height
9440      * @return {Roo.BasicDialog} this
9441      */
9442     setContentSize : function(w, h){
9443         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9444         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9445         //if(!this.el.isBorderBox()){
9446             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9447             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9448         //}
9449         if(this.tabs){
9450             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9451             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9452         }
9453         this.resizeTo(w, h);
9454         return this;
9455     },
9456
9457     /**
9458      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9459      * executed in response to a particular key being pressed while the dialog is active.
9460      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9461      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9462      * @param {Function} fn The function to call
9463      * @param {Object} scope (optional) The scope of the function
9464      * @return {Roo.BasicDialog} this
9465      */
9466     addKeyListener : function(key, fn, scope){
9467         var keyCode, shift, ctrl, alt;
9468         if(typeof key == "object" && !(key instanceof Array)){
9469             keyCode = key["key"];
9470             shift = key["shift"];
9471             ctrl = key["ctrl"];
9472             alt = key["alt"];
9473         }else{
9474             keyCode = key;
9475         }
9476         var handler = function(dlg, e){
9477             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9478                 var k = e.getKey();
9479                 if(keyCode instanceof Array){
9480                     for(var i = 0, len = keyCode.length; i < len; i++){
9481                         if(keyCode[i] == k){
9482                           fn.call(scope || window, dlg, k, e);
9483                           return;
9484                         }
9485                     }
9486                 }else{
9487                     if(k == keyCode){
9488                         fn.call(scope || window, dlg, k, e);
9489                     }
9490                 }
9491             }
9492         };
9493         this.on("keydown", handler);
9494         return this;
9495     },
9496
9497     /**
9498      * Returns the TabPanel component (creates it if it doesn't exist).
9499      * Note: If you wish to simply check for the existence of tabs without creating them,
9500      * check for a null 'tabs' property.
9501      * @return {Roo.TabPanel} The tabs component
9502      */
9503     getTabs : function(){
9504         if(!this.tabs){
9505             this.el.addClass("x-dlg-auto-tabs");
9506             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9507             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9508         }
9509         return this.tabs;
9510     },
9511
9512     /**
9513      * Adds a button to the footer section of the dialog.
9514      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9515      * object or a valid Roo.DomHelper element config
9516      * @param {Function} handler The function called when the button is clicked
9517      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9518      * @return {Roo.Button} The new button
9519      */
9520     addButton : function(config, handler, scope){
9521         var dh = Roo.DomHelper;
9522         if(!this.footer){
9523             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9524         }
9525         if(!this.btnContainer){
9526             var tb = this.footer.createChild({
9527
9528                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9529                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9530             }, null, true);
9531             this.btnContainer = tb.firstChild.firstChild.firstChild;
9532         }
9533         var bconfig = {
9534             handler: handler,
9535             scope: scope,
9536             minWidth: this.minButtonWidth,
9537             hideParent:true
9538         };
9539         if(typeof config == "string"){
9540             bconfig.text = config;
9541         }else{
9542             if(config.tag){
9543                 bconfig.dhconfig = config;
9544             }else{
9545                 Roo.apply(bconfig, config);
9546             }
9547         }
9548         var fc = false;
9549         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9550             bconfig.position = Math.max(0, bconfig.position);
9551             fc = this.btnContainer.childNodes[bconfig.position];
9552         }
9553          
9554         var btn = new Roo.Button(
9555             fc ? 
9556                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9557                 : this.btnContainer.appendChild(document.createElement("td")),
9558             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9559             bconfig
9560         );
9561         this.syncBodyHeight();
9562         if(!this.buttons){
9563             /**
9564              * Array of all the buttons that have been added to this dialog via addButton
9565              * @type Array
9566              */
9567             this.buttons = [];
9568         }
9569         this.buttons.push(btn);
9570         return btn;
9571     },
9572
9573     /**
9574      * Sets the default button to be focused when the dialog is displayed.
9575      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9576      * @return {Roo.BasicDialog} this
9577      */
9578     setDefaultButton : function(btn){
9579         this.defaultButton = btn;
9580         return this;
9581     },
9582
9583     // private
9584     getHeaderFooterHeight : function(safe){
9585         var height = 0;
9586         if(this.header){
9587            height += this.header.getHeight();
9588         }
9589         if(this.footer){
9590            var fm = this.footer.getMargins();
9591             height += (this.footer.getHeight()+fm.top+fm.bottom);
9592         }
9593         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9594         height += this.centerBg.getPadding("tb");
9595         return height;
9596     },
9597
9598     // private
9599     syncBodyHeight : function()
9600     {
9601         var bd = this.body, // the text
9602             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9603             bw = this.bwrap;
9604         var height = this.size.height - this.getHeaderFooterHeight(false);
9605         bd.setHeight(height-bd.getMargins("tb"));
9606         var hh = this.header.getHeight();
9607         var h = this.size.height-hh;
9608         cb.setHeight(h);
9609         
9610         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9611         bw.setHeight(h-cb.getPadding("tb"));
9612         
9613         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9614         bd.setWidth(bw.getWidth(true));
9615         if(this.tabs){
9616             this.tabs.syncHeight();
9617             if(Roo.isIE){
9618                 this.tabs.el.repaint();
9619             }
9620         }
9621     },
9622
9623     /**
9624      * Restores the previous state of the dialog if Roo.state is configured.
9625      * @return {Roo.BasicDialog} this
9626      */
9627     restoreState : function(){
9628         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9629         if(box && box.width){
9630             this.xy = [box.x, box.y];
9631             this.resizeTo(box.width, box.height);
9632         }
9633         return this;
9634     },
9635
9636     // private
9637     beforeShow : function(){
9638         this.expand();
9639         if(this.fixedcenter){
9640             this.xy = this.el.getCenterXY(true);
9641         }
9642         if(this.modal){
9643             Roo.get(document.body).addClass("x-body-masked");
9644             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9645             this.mask.show();
9646         }
9647         this.constrainXY();
9648     },
9649
9650     // private
9651     animShow : function(){
9652         var b = Roo.get(this.animateTarget).getBox();
9653         this.proxy.setSize(b.width, b.height);
9654         this.proxy.setLocation(b.x, b.y);
9655         this.proxy.show();
9656         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9657                     true, .35, this.showEl.createDelegate(this));
9658     },
9659
9660     /**
9661      * Shows the dialog.
9662      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9663      * @return {Roo.BasicDialog} this
9664      */
9665     show : function(animateTarget){
9666         if (this.fireEvent("beforeshow", this) === false){
9667             return;
9668         }
9669         if(this.syncHeightBeforeShow){
9670             this.syncBodyHeight();
9671         }else if(this.firstShow){
9672             this.firstShow = false;
9673             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9674         }
9675         this.animateTarget = animateTarget || this.animateTarget;
9676         if(!this.el.isVisible()){
9677             this.beforeShow();
9678             if(this.animateTarget && Roo.get(this.animateTarget)){
9679                 this.animShow();
9680             }else{
9681                 this.showEl();
9682             }
9683         }
9684         return this;
9685     },
9686
9687     // private
9688     showEl : function(){
9689         this.proxy.hide();
9690         this.el.setXY(this.xy);
9691         this.el.show();
9692         this.adjustAssets(true);
9693         this.toFront();
9694         this.focus();
9695         // IE peekaboo bug - fix found by Dave Fenwick
9696         if(Roo.isIE){
9697             this.el.repaint();
9698         }
9699         this.fireEvent("show", this);
9700     },
9701
9702     /**
9703      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9704      * dialog itself will receive focus.
9705      */
9706     focus : function(){
9707         if(this.defaultButton){
9708             this.defaultButton.focus();
9709         }else{
9710             this.focusEl.focus();
9711         }
9712     },
9713
9714     // private
9715     constrainXY : function(){
9716         if(this.constraintoviewport !== false){
9717             if(!this.viewSize){
9718                 if(this.container){
9719                     var s = this.container.getSize();
9720                     this.viewSize = [s.width, s.height];
9721                 }else{
9722                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9723                 }
9724             }
9725             var s = Roo.get(this.container||document).getScroll();
9726
9727             var x = this.xy[0], y = this.xy[1];
9728             var w = this.size.width, h = this.size.height;
9729             var vw = this.viewSize[0], vh = this.viewSize[1];
9730             // only move it if it needs it
9731             var moved = false;
9732             // first validate right/bottom
9733             if(x + w > vw+s.left){
9734                 x = vw - w;
9735                 moved = true;
9736             }
9737             if(y + h > vh+s.top){
9738                 y = vh - h;
9739                 moved = true;
9740             }
9741             // then make sure top/left isn't negative
9742             if(x < s.left){
9743                 x = s.left;
9744                 moved = true;
9745             }
9746             if(y < s.top){
9747                 y = s.top;
9748                 moved = true;
9749             }
9750             if(moved){
9751                 // cache xy
9752                 this.xy = [x, y];
9753                 if(this.isVisible()){
9754                     this.el.setLocation(x, y);
9755                     this.adjustAssets();
9756                 }
9757             }
9758         }
9759     },
9760
9761     // private
9762     onDrag : function(){
9763         if(!this.proxyDrag){
9764             this.xy = this.el.getXY();
9765             this.adjustAssets();
9766         }
9767     },
9768
9769     // private
9770     adjustAssets : function(doShow){
9771         var x = this.xy[0], y = this.xy[1];
9772         var w = this.size.width, h = this.size.height;
9773         if(doShow === true){
9774             if(this.shadow){
9775                 this.shadow.show(this.el);
9776             }
9777             if(this.shim){
9778                 this.shim.show();
9779             }
9780         }
9781         if(this.shadow && this.shadow.isVisible()){
9782             this.shadow.show(this.el);
9783         }
9784         if(this.shim && this.shim.isVisible()){
9785             this.shim.setBounds(x, y, w, h);
9786         }
9787     },
9788
9789     // private
9790     adjustViewport : function(w, h){
9791         if(!w || !h){
9792             w = Roo.lib.Dom.getViewWidth();
9793             h = Roo.lib.Dom.getViewHeight();
9794         }
9795         // cache the size
9796         this.viewSize = [w, h];
9797         if(this.modal && this.mask.isVisible()){
9798             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9799             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9800         }
9801         if(this.isVisible()){
9802             this.constrainXY();
9803         }
9804     },
9805
9806     /**
9807      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9808      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9809      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9810      */
9811     destroy : function(removeEl){
9812         if(this.isVisible()){
9813             this.animateTarget = null;
9814             this.hide();
9815         }
9816         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9817         if(this.tabs){
9818             this.tabs.destroy(removeEl);
9819         }
9820         Roo.destroy(
9821              this.shim,
9822              this.proxy,
9823              this.resizer,
9824              this.close,
9825              this.mask
9826         );
9827         if(this.dd){
9828             this.dd.unreg();
9829         }
9830         if(this.buttons){
9831            for(var i = 0, len = this.buttons.length; i < len; i++){
9832                this.buttons[i].destroy();
9833            }
9834         }
9835         this.el.removeAllListeners();
9836         if(removeEl === true){
9837             this.el.update("");
9838             this.el.remove();
9839         }
9840         Roo.DialogManager.unregister(this);
9841     },
9842
9843     // private
9844     startMove : function(){
9845         if(this.proxyDrag){
9846             this.proxy.show();
9847         }
9848         if(this.constraintoviewport !== false){
9849             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9850         }
9851     },
9852
9853     // private
9854     endMove : function(){
9855         if(!this.proxyDrag){
9856             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9857         }else{
9858             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9859             this.proxy.hide();
9860         }
9861         this.refreshSize();
9862         this.adjustAssets();
9863         this.focus();
9864         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9865     },
9866
9867     /**
9868      * Brings this dialog to the front of any other visible dialogs
9869      * @return {Roo.BasicDialog} this
9870      */
9871     toFront : function(){
9872         Roo.DialogManager.bringToFront(this);
9873         return this;
9874     },
9875
9876     /**
9877      * Sends this dialog to the back (under) of any other visible dialogs
9878      * @return {Roo.BasicDialog} this
9879      */
9880     toBack : function(){
9881         Roo.DialogManager.sendToBack(this);
9882         return this;
9883     },
9884
9885     /**
9886      * Centers this dialog in the viewport
9887      * @return {Roo.BasicDialog} this
9888      */
9889     center : function(){
9890         var xy = this.el.getCenterXY(true);
9891         this.moveTo(xy[0], xy[1]);
9892         return this;
9893     },
9894
9895     /**
9896      * Moves the dialog's top-left corner to the specified point
9897      * @param {Number} x
9898      * @param {Number} y
9899      * @return {Roo.BasicDialog} this
9900      */
9901     moveTo : function(x, y){
9902         this.xy = [x,y];
9903         if(this.isVisible()){
9904             this.el.setXY(this.xy);
9905             this.adjustAssets();
9906         }
9907         return this;
9908     },
9909
9910     /**
9911      * Aligns the dialog to the specified element
9912      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9913      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9914      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9915      * @return {Roo.BasicDialog} this
9916      */
9917     alignTo : function(element, position, offsets){
9918         this.xy = this.el.getAlignToXY(element, position, offsets);
9919         if(this.isVisible()){
9920             this.el.setXY(this.xy);
9921             this.adjustAssets();
9922         }
9923         return this;
9924     },
9925
9926     /**
9927      * Anchors an element to another element and realigns it when the window is resized.
9928      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9929      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9930      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9931      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9932      * is a number, it is used as the buffer delay (defaults to 50ms).
9933      * @return {Roo.BasicDialog} this
9934      */
9935     anchorTo : function(el, alignment, offsets, monitorScroll){
9936         var action = function(){
9937             this.alignTo(el, alignment, offsets);
9938         };
9939         Roo.EventManager.onWindowResize(action, this);
9940         var tm = typeof monitorScroll;
9941         if(tm != 'undefined'){
9942             Roo.EventManager.on(window, 'scroll', action, this,
9943                 {buffer: tm == 'number' ? monitorScroll : 50});
9944         }
9945         action.call(this);
9946         return this;
9947     },
9948
9949     /**
9950      * Returns true if the dialog is visible
9951      * @return {Boolean}
9952      */
9953     isVisible : function(){
9954         return this.el.isVisible();
9955     },
9956
9957     // private
9958     animHide : function(callback){
9959         var b = Roo.get(this.animateTarget).getBox();
9960         this.proxy.show();
9961         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9962         this.el.hide();
9963         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9964                     this.hideEl.createDelegate(this, [callback]));
9965     },
9966
9967     /**
9968      * Hides the dialog.
9969      * @param {Function} callback (optional) Function to call when the dialog is hidden
9970      * @return {Roo.BasicDialog} this
9971      */
9972     hide : function(callback){
9973         if (this.fireEvent("beforehide", this) === false){
9974             return;
9975         }
9976         if(this.shadow){
9977             this.shadow.hide();
9978         }
9979         if(this.shim) {
9980           this.shim.hide();
9981         }
9982         // sometimes animateTarget seems to get set.. causing problems...
9983         // this just double checks..
9984         if(this.animateTarget && Roo.get(this.animateTarget)) {
9985            this.animHide(callback);
9986         }else{
9987             this.el.hide();
9988             this.hideEl(callback);
9989         }
9990         return this;
9991     },
9992
9993     // private
9994     hideEl : function(callback){
9995         this.proxy.hide();
9996         if(this.modal){
9997             this.mask.hide();
9998             Roo.get(document.body).removeClass("x-body-masked");
9999         }
10000         this.fireEvent("hide", this);
10001         if(typeof callback == "function"){
10002             callback();
10003         }
10004     },
10005
10006     // private
10007     hideAction : function(){
10008         this.setLeft("-10000px");
10009         this.setTop("-10000px");
10010         this.setStyle("visibility", "hidden");
10011     },
10012
10013     // private
10014     refreshSize : function(){
10015         this.size = this.el.getSize();
10016         this.xy = this.el.getXY();
10017         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10018     },
10019
10020     // private
10021     // z-index is managed by the DialogManager and may be overwritten at any time
10022     setZIndex : function(index){
10023         if(this.modal){
10024             this.mask.setStyle("z-index", index);
10025         }
10026         if(this.shim){
10027             this.shim.setStyle("z-index", ++index);
10028         }
10029         if(this.shadow){
10030             this.shadow.setZIndex(++index);
10031         }
10032         this.el.setStyle("z-index", ++index);
10033         if(this.proxy){
10034             this.proxy.setStyle("z-index", ++index);
10035         }
10036         if(this.resizer){
10037             this.resizer.proxy.setStyle("z-index", ++index);
10038         }
10039
10040         this.lastZIndex = index;
10041     },
10042
10043     /**
10044      * Returns the element for this dialog
10045      * @return {Roo.Element} The underlying dialog Element
10046      */
10047     getEl : function(){
10048         return this.el;
10049     }
10050 });
10051
10052 /**
10053  * @class Roo.DialogManager
10054  * Provides global access to BasicDialogs that have been created and
10055  * support for z-indexing (layering) multiple open dialogs.
10056  */
10057 Roo.DialogManager = function(){
10058     var list = {};
10059     var accessList = [];
10060     var front = null;
10061
10062     // private
10063     var sortDialogs = function(d1, d2){
10064         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10065     };
10066
10067     // private
10068     var orderDialogs = function(){
10069         accessList.sort(sortDialogs);
10070         var seed = Roo.DialogManager.zseed;
10071         for(var i = 0, len = accessList.length; i < len; i++){
10072             var dlg = accessList[i];
10073             if(dlg){
10074                 dlg.setZIndex(seed + (i*10));
10075             }
10076         }
10077     };
10078
10079     return {
10080         /**
10081          * The starting z-index for BasicDialogs (defaults to 9000)
10082          * @type Number The z-index value
10083          */
10084         zseed : 9000,
10085
10086         // private
10087         register : function(dlg){
10088             list[dlg.id] = dlg;
10089             accessList.push(dlg);
10090         },
10091
10092         // private
10093         unregister : function(dlg){
10094             delete list[dlg.id];
10095             var i=0;
10096             var len=0;
10097             if(!accessList.indexOf){
10098                 for(  i = 0, len = accessList.length; i < len; i++){
10099                     if(accessList[i] == dlg){
10100                         accessList.splice(i, 1);
10101                         return;
10102                     }
10103                 }
10104             }else{
10105                  i = accessList.indexOf(dlg);
10106                 if(i != -1){
10107                     accessList.splice(i, 1);
10108                 }
10109             }
10110         },
10111
10112         /**
10113          * Gets a registered dialog by id
10114          * @param {String/Object} id The id of the dialog or a dialog
10115          * @return {Roo.BasicDialog} this
10116          */
10117         get : function(id){
10118             return typeof id == "object" ? id : list[id];
10119         },
10120
10121         /**
10122          * Brings the specified dialog to the front
10123          * @param {String/Object} dlg The id of the dialog or a dialog
10124          * @return {Roo.BasicDialog} this
10125          */
10126         bringToFront : function(dlg){
10127             dlg = this.get(dlg);
10128             if(dlg != front){
10129                 front = dlg;
10130                 dlg._lastAccess = new Date().getTime();
10131                 orderDialogs();
10132             }
10133             return dlg;
10134         },
10135
10136         /**
10137          * Sends the specified dialog to the back
10138          * @param {String/Object} dlg The id of the dialog or a dialog
10139          * @return {Roo.BasicDialog} this
10140          */
10141         sendToBack : function(dlg){
10142             dlg = this.get(dlg);
10143             dlg._lastAccess = -(new Date().getTime());
10144             orderDialogs();
10145             return dlg;
10146         },
10147
10148         /**
10149          * Hides all dialogs
10150          */
10151         hideAll : function(){
10152             for(var id in list){
10153                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10154                     list[id].hide();
10155                 }
10156             }
10157         }
10158     };
10159 }();
10160
10161 /**
10162  * @class Roo.LayoutDialog
10163  * @extends Roo.BasicDialog
10164  * @children Roo.ContentPanel
10165  * @parent builder none
10166  * Dialog which provides adjustments for working with a layout in a Dialog.
10167  * Add your necessary layout config options to the dialog's config.<br>
10168  * Example usage (including a nested layout):
10169  * <pre><code>
10170 if(!dialog){
10171     dialog = new Roo.LayoutDialog("download-dlg", {
10172         modal: true,
10173         width:600,
10174         height:450,
10175         shadow:true,
10176         minWidth:500,
10177         minHeight:350,
10178         autoTabs:true,
10179         proxyDrag:true,
10180         // layout config merges with the dialog config
10181         center:{
10182             tabPosition: "top",
10183             alwaysShowTabs: true
10184         }
10185     });
10186     dialog.addKeyListener(27, dialog.hide, dialog);
10187     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10188     dialog.addButton("Build It!", this.getDownload, this);
10189
10190     // we can even add nested layouts
10191     var innerLayout = new Roo.BorderLayout("dl-inner", {
10192         east: {
10193             initialSize: 200,
10194             autoScroll:true,
10195             split:true
10196         },
10197         center: {
10198             autoScroll:true
10199         }
10200     });
10201     innerLayout.beginUpdate();
10202     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10203     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10204     innerLayout.endUpdate(true);
10205
10206     var layout = dialog.getLayout();
10207     layout.beginUpdate();
10208     layout.add("center", new Roo.ContentPanel("standard-panel",
10209                         {title: "Download the Source", fitToFrame:true}));
10210     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10211                {title: "Build your own roo.js"}));
10212     layout.getRegion("center").showPanel(sp);
10213     layout.endUpdate();
10214 }
10215 </code></pre>
10216     * @constructor
10217     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10218     * @param {Object} config configuration options
10219   */
10220 Roo.LayoutDialog = function(el, cfg){
10221     
10222     var config=  cfg;
10223     if (typeof(cfg) == 'undefined') {
10224         config = Roo.apply({}, el);
10225         // not sure why we use documentElement here.. - it should always be body.
10226         // IE7 borks horribly if we use documentElement.
10227         // webkit also does not like documentElement - it creates a body element...
10228         el = Roo.get( document.body || document.documentElement ).createChild();
10229         //config.autoCreate = true;
10230     }
10231     
10232     
10233     config.autoTabs = false;
10234     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10235     this.body.setStyle({overflow:"hidden", position:"relative"});
10236     this.layout = new Roo.BorderLayout(this.body.dom, config);
10237     this.layout.monitorWindowResize = false;
10238     this.el.addClass("x-dlg-auto-layout");
10239     // fix case when center region overwrites center function
10240     this.center = Roo.BasicDialog.prototype.center;
10241     this.on("show", this.layout.layout, this.layout, true);
10242     if (config.items) {
10243         var xitems = config.items;
10244         delete config.items;
10245         Roo.each(xitems, this.addxtype, this);
10246     }
10247     
10248     
10249 };
10250 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10251     
10252     
10253     /**
10254      * @cfg {Roo.LayoutRegion} east  
10255      */
10256     /**
10257      * @cfg {Roo.LayoutRegion} west
10258      */
10259     /**
10260      * @cfg {Roo.LayoutRegion} south
10261      */
10262     /**
10263      * @cfg {Roo.LayoutRegion} north
10264      */
10265     /**
10266      * @cfg {Roo.LayoutRegion} center
10267      */
10268     /**
10269      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10270      */
10271     
10272     
10273     /**
10274      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10275      * @deprecated
10276      */
10277     endUpdate : function(){
10278         this.layout.endUpdate();
10279     },
10280
10281     /**
10282      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10283      *  @deprecated
10284      */
10285     beginUpdate : function(){
10286         this.layout.beginUpdate();
10287     },
10288
10289     /**
10290      * Get the BorderLayout for this dialog
10291      * @return {Roo.BorderLayout}
10292      */
10293     getLayout : function(){
10294         return this.layout;
10295     },
10296
10297     showEl : function(){
10298         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10299         if(Roo.isIE7){
10300             this.layout.layout();
10301         }
10302     },
10303
10304     // private
10305     // Use the syncHeightBeforeShow config option to control this automatically
10306     syncBodyHeight : function(){
10307         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10308         if(this.layout){this.layout.layout();}
10309     },
10310     
10311       /**
10312      * Add an xtype element (actually adds to the layout.)
10313      * @return {Object} xdata xtype object data.
10314      */
10315     
10316     addxtype : function(c) {
10317         return this.layout.addxtype(c);
10318     }
10319 });/*
10320  * Based on:
10321  * Ext JS Library 1.1.1
10322  * Copyright(c) 2006-2007, Ext JS, LLC.
10323  *
10324  * Originally Released Under LGPL - original licence link has changed is not relivant.
10325  *
10326  * Fork - LGPL
10327  * <script type="text/javascript">
10328  */
10329  
10330 /**
10331  * @class Roo.MessageBox
10332  * @static
10333  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10334  * Example usage:
10335  *<pre><code>
10336 // Basic alert:
10337 Roo.Msg.alert('Status', 'Changes saved successfully.');
10338
10339 // Prompt for user data:
10340 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10341     if (btn == 'ok'){
10342         // process text value...
10343     }
10344 });
10345
10346 // Show a dialog using config options:
10347 Roo.Msg.show({
10348    title:'Save Changes?',
10349    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10350    buttons: Roo.Msg.YESNOCANCEL,
10351    fn: processResult,
10352    animEl: 'elId'
10353 });
10354 </code></pre>
10355  * @static
10356  */
10357 Roo.MessageBox = function(){
10358     var dlg, opt, mask, waitTimer;
10359     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10360     var buttons, activeTextEl, bwidth;
10361
10362     // private
10363     var handleButton = function(button){
10364         dlg.hide();
10365         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10366     };
10367
10368     // private
10369     var handleHide = function(){
10370         if(opt && opt.cls){
10371             dlg.el.removeClass(opt.cls);
10372         }
10373         if(waitTimer){
10374             Roo.TaskMgr.stop(waitTimer);
10375             waitTimer = null;
10376         }
10377     };
10378
10379     // private
10380     var updateButtons = function(b){
10381         var width = 0;
10382         if(!b){
10383             buttons["ok"].hide();
10384             buttons["cancel"].hide();
10385             buttons["yes"].hide();
10386             buttons["no"].hide();
10387             dlg.footer.dom.style.display = 'none';
10388             return width;
10389         }
10390         dlg.footer.dom.style.display = '';
10391         for(var k in buttons){
10392             if(typeof buttons[k] != "function"){
10393                 if(b[k]){
10394                     buttons[k].show();
10395                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10396                     width += buttons[k].el.getWidth()+15;
10397                 }else{
10398                     buttons[k].hide();
10399                 }
10400             }
10401         }
10402         return width;
10403     };
10404
10405     // private
10406     var handleEsc = function(d, k, e){
10407         if(opt && opt.closable !== false){
10408             dlg.hide();
10409         }
10410         if(e){
10411             e.stopEvent();
10412         }
10413     };
10414
10415     return {
10416         /**
10417          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10418          * @return {Roo.BasicDialog} The BasicDialog element
10419          */
10420         getDialog : function(){
10421            if(!dlg){
10422                 dlg = new Roo.BasicDialog("x-msg-box", {
10423                     autoCreate : true,
10424                     shadow: true,
10425                     draggable: true,
10426                     resizable:false,
10427                     constraintoviewport:false,
10428                     fixedcenter:true,
10429                     collapsible : false,
10430                     shim:true,
10431                     modal: true,
10432                     width:400, height:100,
10433                     buttonAlign:"center",
10434                     closeClick : function(){
10435                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10436                             handleButton("no");
10437                         }else{
10438                             handleButton("cancel");
10439                         }
10440                     }
10441                 });
10442               
10443                 dlg.on("hide", handleHide);
10444                 mask = dlg.mask;
10445                 dlg.addKeyListener(27, handleEsc);
10446                 buttons = {};
10447                 var bt = this.buttonText;
10448                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10449                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10450                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10451                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10452                 bodyEl = dlg.body.createChild({
10453
10454                     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>'
10455                 });
10456                 msgEl = bodyEl.dom.firstChild;
10457                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10458                 textboxEl.enableDisplayMode();
10459                 textboxEl.addKeyListener([10,13], function(){
10460                     if(dlg.isVisible() && opt && opt.buttons){
10461                         if(opt.buttons.ok){
10462                             handleButton("ok");
10463                         }else if(opt.buttons.yes){
10464                             handleButton("yes");
10465                         }
10466                     }
10467                 });
10468                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10469                 textareaEl.enableDisplayMode();
10470                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10471                 progressEl.enableDisplayMode();
10472                 var pf = progressEl.dom.firstChild;
10473                 if (pf) {
10474                     pp = Roo.get(pf.firstChild);
10475                     pp.setHeight(pf.offsetHeight);
10476                 }
10477                 
10478             }
10479             return dlg;
10480         },
10481
10482         /**
10483          * Updates the message box body text
10484          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10485          * the XHTML-compliant non-breaking space character '&amp;#160;')
10486          * @return {Roo.MessageBox} This message box
10487          */
10488         updateText : function(text){
10489             if(!dlg.isVisible() && !opt.width){
10490                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10491             }
10492             msgEl.innerHTML = text || '&#160;';
10493       
10494             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10495             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10496             var w = Math.max(
10497                     Math.min(opt.width || cw , this.maxWidth), 
10498                     Math.max(opt.minWidth || this.minWidth, bwidth)
10499             );
10500             if(opt.prompt){
10501                 activeTextEl.setWidth(w);
10502             }
10503             if(dlg.isVisible()){
10504                 dlg.fixedcenter = false;
10505             }
10506             // to big, make it scroll. = But as usual stupid IE does not support
10507             // !important..
10508             
10509             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10510                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10511                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10512             } else {
10513                 bodyEl.dom.style.height = '';
10514                 bodyEl.dom.style.overflowY = '';
10515             }
10516             if (cw > w) {
10517                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10518             } else {
10519                 bodyEl.dom.style.overflowX = '';
10520             }
10521             
10522             dlg.setContentSize(w, bodyEl.getHeight());
10523             if(dlg.isVisible()){
10524                 dlg.fixedcenter = true;
10525             }
10526             return this;
10527         },
10528
10529         /**
10530          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10531          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10532          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10533          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10534          * @return {Roo.MessageBox} This message box
10535          */
10536         updateProgress : function(value, text){
10537             if(text){
10538                 this.updateText(text);
10539             }
10540             if (pp) { // weird bug on my firefox - for some reason this is not defined
10541                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10542             }
10543             return this;
10544         },        
10545
10546         /**
10547          * Returns true if the message box is currently displayed
10548          * @return {Boolean} True if the message box is visible, else false
10549          */
10550         isVisible : function(){
10551             return dlg && dlg.isVisible();  
10552         },
10553
10554         /**
10555          * Hides the message box if it is displayed
10556          */
10557         hide : function(){
10558             if(this.isVisible()){
10559                 dlg.hide();
10560             }  
10561         },
10562
10563         /**
10564          * Displays a new message box, or reinitializes an existing message box, based on the config options
10565          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10566          * The following config object properties are supported:
10567          * <pre>
10568 Property    Type             Description
10569 ----------  ---------------  ------------------------------------------------------------------------------------
10570 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10571                                    closes (defaults to undefined)
10572 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10573                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10574 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10575                                    progress and wait dialogs will ignore this property and always hide the
10576                                    close button as they can only be closed programmatically.
10577 cls               String           A custom CSS class to apply to the message box element
10578 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10579                                    displayed (defaults to 75)
10580 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10581                                    function will be btn (the name of the button that was clicked, if applicable,
10582                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10583                                    Progress and wait dialogs will ignore this option since they do not respond to
10584                                    user actions and can only be closed programmatically, so any required function
10585                                    should be called by the same code after it closes the dialog.
10586 icon              String           A CSS class that provides a background image to be used as an icon for
10587                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10588 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10589 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10590 modal             Boolean          False to allow user interaction with the page while the message box is
10591                                    displayed (defaults to true)
10592 msg               String           A string that will replace the existing message box body text (defaults
10593                                    to the XHTML-compliant non-breaking space character '&#160;')
10594 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10595 progress          Boolean          True to display a progress bar (defaults to false)
10596 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10597 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10598 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10599 title             String           The title text
10600 value             String           The string value to set into the active textbox element if displayed
10601 wait              Boolean          True to display a progress bar (defaults to false)
10602 width             Number           The width of the dialog in pixels
10603 </pre>
10604          *
10605          * Example usage:
10606          * <pre><code>
10607 Roo.Msg.show({
10608    title: 'Address',
10609    msg: 'Please enter your address:',
10610    width: 300,
10611    buttons: Roo.MessageBox.OKCANCEL,
10612    multiline: true,
10613    fn: saveAddress,
10614    animEl: 'addAddressBtn'
10615 });
10616 </code></pre>
10617          * @param {Object} config Configuration options
10618          * @return {Roo.MessageBox} This message box
10619          */
10620         show : function(options)
10621         {
10622             
10623             // this causes nightmares if you show one dialog after another
10624             // especially on callbacks..
10625              
10626             if(this.isVisible()){
10627                 
10628                 this.hide();
10629                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10630                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10631                 Roo.log("New Dialog Message:" +  options.msg )
10632                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10633                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10634                 
10635             }
10636             var d = this.getDialog();
10637             opt = options;
10638             d.setTitle(opt.title || "&#160;");
10639             d.close.setDisplayed(opt.closable !== false);
10640             activeTextEl = textboxEl;
10641             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10642             if(opt.prompt){
10643                 if(opt.multiline){
10644                     textboxEl.hide();
10645                     textareaEl.show();
10646                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10647                         opt.multiline : this.defaultTextHeight);
10648                     activeTextEl = textareaEl;
10649                 }else{
10650                     textboxEl.show();
10651                     textareaEl.hide();
10652                 }
10653             }else{
10654                 textboxEl.hide();
10655                 textareaEl.hide();
10656             }
10657             progressEl.setDisplayed(opt.progress === true);
10658             this.updateProgress(0);
10659             activeTextEl.dom.value = opt.value || "";
10660             if(opt.prompt){
10661                 dlg.setDefaultButton(activeTextEl);
10662             }else{
10663                 var bs = opt.buttons;
10664                 var db = null;
10665                 if(bs && bs.ok){
10666                     db = buttons["ok"];
10667                 }else if(bs && bs.yes){
10668                     db = buttons["yes"];
10669                 }
10670                 dlg.setDefaultButton(db);
10671             }
10672             bwidth = updateButtons(opt.buttons);
10673             this.updateText(opt.msg);
10674             if(opt.cls){
10675                 d.el.addClass(opt.cls);
10676             }
10677             d.proxyDrag = opt.proxyDrag === true;
10678             d.modal = opt.modal !== false;
10679             d.mask = opt.modal !== false ? mask : false;
10680             if(!d.isVisible()){
10681                 // force it to the end of the z-index stack so it gets a cursor in FF
10682                 document.body.appendChild(dlg.el.dom);
10683                 d.animateTarget = null;
10684                 d.show(options.animEl);
10685             }
10686             dlg.toFront();
10687             return this;
10688         },
10689
10690         /**
10691          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10692          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10693          * and closing the message box when the process is complete.
10694          * @param {String} title The title bar text
10695          * @param {String} msg The message box body text
10696          * @return {Roo.MessageBox} This message box
10697          */
10698         progress : function(title, msg){
10699             this.show({
10700                 title : title,
10701                 msg : msg,
10702                 buttons: false,
10703                 progress:true,
10704                 closable:false,
10705                 minWidth: this.minProgressWidth,
10706                 modal : true
10707             });
10708             return this;
10709         },
10710
10711         /**
10712          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10713          * If a callback function is passed it will be called after the user clicks the button, and the
10714          * id of the button that was clicked will be passed as the only parameter to the callback
10715          * (could also be the top-right close button).
10716          * @param {String} title The title bar text
10717          * @param {String} msg The message box body text
10718          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10719          * @param {Object} scope (optional) The scope of the callback function
10720          * @return {Roo.MessageBox} This message box
10721          */
10722         alert : function(title, msg, fn, scope){
10723             this.show({
10724                 title : title,
10725                 msg : msg,
10726                 buttons: this.OK,
10727                 fn: fn,
10728                 scope : scope,
10729                 modal : true
10730             });
10731             return this;
10732         },
10733
10734         /**
10735          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10736          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10737          * You are responsible for closing the message box when the process is complete.
10738          * @param {String} msg The message box body text
10739          * @param {String} title (optional) The title bar text
10740          * @return {Roo.MessageBox} This message box
10741          */
10742         wait : function(msg, title){
10743             this.show({
10744                 title : title,
10745                 msg : msg,
10746                 buttons: false,
10747                 closable:false,
10748                 progress:true,
10749                 modal:true,
10750                 width:300,
10751                 wait:true
10752             });
10753             waitTimer = Roo.TaskMgr.start({
10754                 run: function(i){
10755                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10756                 },
10757                 interval: 1000
10758             });
10759             return this;
10760         },
10761
10762         /**
10763          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10764          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10765          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10766          * @param {String} title The title bar text
10767          * @param {String} msg The message box body text
10768          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10769          * @param {Object} scope (optional) The scope of the callback function
10770          * @return {Roo.MessageBox} This message box
10771          */
10772         confirm : function(title, msg, fn, scope){
10773             this.show({
10774                 title : title,
10775                 msg : msg,
10776                 buttons: this.YESNO,
10777                 fn: fn,
10778                 scope : scope,
10779                 modal : true
10780             });
10781             return this;
10782         },
10783
10784         /**
10785          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10786          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10787          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10788          * (could also be the top-right close button) and the text that was entered will be passed as the two
10789          * parameters to the callback.
10790          * @param {String} title The title bar text
10791          * @param {String} msg The message box body text
10792          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10793          * @param {Object} scope (optional) The scope of the callback function
10794          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10795          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10796          * @return {Roo.MessageBox} This message box
10797          */
10798         prompt : function(title, msg, fn, scope, multiline){
10799             this.show({
10800                 title : title,
10801                 msg : msg,
10802                 buttons: this.OKCANCEL,
10803                 fn: fn,
10804                 minWidth:250,
10805                 scope : scope,
10806                 prompt:true,
10807                 multiline: multiline,
10808                 modal : true
10809             });
10810             return this;
10811         },
10812
10813         /**
10814          * Button config that displays a single OK button
10815          * @type Object
10816          */
10817         OK : {ok:true},
10818         /**
10819          * Button config that displays Yes and No buttons
10820          * @type Object
10821          */
10822         YESNO : {yes:true, no:true},
10823         /**
10824          * Button config that displays OK and Cancel buttons
10825          * @type Object
10826          */
10827         OKCANCEL : {ok:true, cancel:true},
10828         /**
10829          * Button config that displays Yes, No and Cancel buttons
10830          * @type Object
10831          */
10832         YESNOCANCEL : {yes:true, no:true, cancel:true},
10833
10834         /**
10835          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10836          * @type Number
10837          */
10838         defaultTextHeight : 75,
10839         /**
10840          * The maximum width in pixels of the message box (defaults to 600)
10841          * @type Number
10842          */
10843         maxWidth : 600,
10844         /**
10845          * The minimum width in pixels of the message box (defaults to 100)
10846          * @type Number
10847          */
10848         minWidth : 100,
10849         /**
10850          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10851          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10852          * @type Number
10853          */
10854         minProgressWidth : 250,
10855         /**
10856          * An object containing the default button text strings that can be overriden for localized language support.
10857          * Supported properties are: ok, cancel, yes and no.
10858          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10859          * @type Object
10860          */
10861         buttonText : {
10862             ok : "OK",
10863             cancel : "Cancel",
10864             yes : "Yes",
10865             no : "No"
10866         }
10867     };
10868 }();
10869
10870 /**
10871  * Shorthand for {@link Roo.MessageBox}
10872  */
10873 Roo.Msg = Roo.MessageBox;/*
10874  * Based on:
10875  * Ext JS Library 1.1.1
10876  * Copyright(c) 2006-2007, Ext JS, LLC.
10877  *
10878  * Originally Released Under LGPL - original licence link has changed is not relivant.
10879  *
10880  * Fork - LGPL
10881  * <script type="text/javascript">
10882  */
10883 /**
10884  * @class Roo.QuickTips
10885  * Provides attractive and customizable tooltips for any element.
10886  * @static
10887  */
10888 Roo.QuickTips = function(){
10889     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10890     var ce, bd, xy, dd;
10891     var visible = false, disabled = true, inited = false;
10892     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10893     
10894     var onOver = function(e){
10895         if(disabled){
10896             return;
10897         }
10898         var t = e.getTarget();
10899         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10900             return;
10901         }
10902         if(ce && t == ce.el){
10903             clearTimeout(hideProc);
10904             return;
10905         }
10906         if(t && tagEls[t.id]){
10907             tagEls[t.id].el = t;
10908             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10909             return;
10910         }
10911         var ttp, et = Roo.fly(t);
10912         var ns = cfg.namespace;
10913         if(tm.interceptTitles && t.title){
10914             ttp = t.title;
10915             t.qtip = ttp;
10916             t.removeAttribute("title");
10917             e.preventDefault();
10918         }else{
10919             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10920         }
10921         if(ttp){
10922             showProc = show.defer(tm.showDelay, tm, [{
10923                 el: t, 
10924                 text: ttp.replace(/\\n/g,'<br/>'),
10925                 width: et.getAttributeNS(ns, cfg.width),
10926                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10927                 title: et.getAttributeNS(ns, cfg.title),
10928                     cls: et.getAttributeNS(ns, cfg.cls)
10929             }]);
10930         }
10931     };
10932     
10933     var onOut = function(e){
10934         clearTimeout(showProc);
10935         var t = e.getTarget();
10936         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10937             hideProc = setTimeout(hide, tm.hideDelay);
10938         }
10939     };
10940     
10941     var onMove = function(e){
10942         if(disabled){
10943             return;
10944         }
10945         xy = e.getXY();
10946         xy[1] += 18;
10947         if(tm.trackMouse && ce){
10948             el.setXY(xy);
10949         }
10950     };
10951     
10952     var onDown = function(e){
10953         clearTimeout(showProc);
10954         clearTimeout(hideProc);
10955         if(!e.within(el)){
10956             if(tm.hideOnClick){
10957                 hide();
10958                 tm.disable();
10959                 tm.enable.defer(100, tm);
10960             }
10961         }
10962     };
10963     
10964     var getPad = function(){
10965         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10966     };
10967
10968     var show = function(o){
10969         if(disabled){
10970             return;
10971         }
10972         clearTimeout(dismissProc);
10973         ce = o;
10974         if(removeCls){ // in case manually hidden
10975             el.removeClass(removeCls);
10976             removeCls = null;
10977         }
10978         if(ce.cls){
10979             el.addClass(ce.cls);
10980             removeCls = ce.cls;
10981         }
10982         if(ce.title){
10983             tipTitle.update(ce.title);
10984             tipTitle.show();
10985         }else{
10986             tipTitle.update('');
10987             tipTitle.hide();
10988         }
10989         el.dom.style.width  = tm.maxWidth+'px';
10990         //tipBody.dom.style.width = '';
10991         tipBodyText.update(o.text);
10992         var p = getPad(), w = ce.width;
10993         if(!w){
10994             var td = tipBodyText.dom;
10995             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10996             if(aw > tm.maxWidth){
10997                 w = tm.maxWidth;
10998             }else if(aw < tm.minWidth){
10999                 w = tm.minWidth;
11000             }else{
11001                 w = aw;
11002             }
11003         }
11004         //tipBody.setWidth(w);
11005         el.setWidth(parseInt(w, 10) + p);
11006         if(ce.autoHide === false){
11007             close.setDisplayed(true);
11008             if(dd){
11009                 dd.unlock();
11010             }
11011         }else{
11012             close.setDisplayed(false);
11013             if(dd){
11014                 dd.lock();
11015             }
11016         }
11017         if(xy){
11018             el.avoidY = xy[1]-18;
11019             el.setXY(xy);
11020         }
11021         if(tm.animate){
11022             el.setOpacity(.1);
11023             el.setStyle("visibility", "visible");
11024             el.fadeIn({callback: afterShow});
11025         }else{
11026             afterShow();
11027         }
11028     };
11029     
11030     var afterShow = function(){
11031         if(ce){
11032             el.show();
11033             esc.enable();
11034             if(tm.autoDismiss && ce.autoHide !== false){
11035                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11036             }
11037         }
11038     };
11039     
11040     var hide = function(noanim){
11041         clearTimeout(dismissProc);
11042         clearTimeout(hideProc);
11043         ce = null;
11044         if(el.isVisible()){
11045             esc.disable();
11046             if(noanim !== true && tm.animate){
11047                 el.fadeOut({callback: afterHide});
11048             }else{
11049                 afterHide();
11050             } 
11051         }
11052     };
11053     
11054     var afterHide = function(){
11055         el.hide();
11056         if(removeCls){
11057             el.removeClass(removeCls);
11058             removeCls = null;
11059         }
11060     };
11061     
11062     return {
11063         /**
11064         * @cfg {Number} minWidth
11065         * The minimum width of the quick tip (defaults to 40)
11066         */
11067        minWidth : 40,
11068         /**
11069         * @cfg {Number} maxWidth
11070         * The maximum width of the quick tip (defaults to 300)
11071         */
11072        maxWidth : 300,
11073         /**
11074         * @cfg {Boolean} interceptTitles
11075         * True to automatically use the element's DOM title value if available (defaults to false)
11076         */
11077        interceptTitles : false,
11078         /**
11079         * @cfg {Boolean} trackMouse
11080         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11081         */
11082        trackMouse : false,
11083         /**
11084         * @cfg {Boolean} hideOnClick
11085         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11086         */
11087        hideOnClick : true,
11088         /**
11089         * @cfg {Number} showDelay
11090         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11091         */
11092        showDelay : 500,
11093         /**
11094         * @cfg {Number} hideDelay
11095         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11096         */
11097        hideDelay : 200,
11098         /**
11099         * @cfg {Boolean} autoHide
11100         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11101         * Used in conjunction with hideDelay.
11102         */
11103        autoHide : true,
11104         /**
11105         * @cfg {Boolean}
11106         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11107         * (defaults to true).  Used in conjunction with autoDismissDelay.
11108         */
11109        autoDismiss : true,
11110         /**
11111         * @cfg {Number}
11112         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11113         */
11114        autoDismissDelay : 5000,
11115        /**
11116         * @cfg {Boolean} animate
11117         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11118         */
11119        animate : false,
11120
11121        /**
11122         * @cfg {String} title
11123         * Title text to display (defaults to '').  This can be any valid HTML markup.
11124         */
11125         title: '',
11126        /**
11127         * @cfg {String} text
11128         * Body text to display (defaults to '').  This can be any valid HTML markup.
11129         */
11130         text : '',
11131        /**
11132         * @cfg {String} cls
11133         * A CSS class to apply to the base quick tip element (defaults to '').
11134         */
11135         cls : '',
11136        /**
11137         * @cfg {Number} width
11138         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11139         * minWidth or maxWidth.
11140         */
11141         width : null,
11142
11143     /**
11144      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11145      * or display QuickTips in a page.
11146      */
11147        init : function(){
11148           tm = Roo.QuickTips;
11149           cfg = tm.tagConfig;
11150           if(!inited){
11151               if(!Roo.isReady){ // allow calling of init() before onReady
11152                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11153                   return;
11154               }
11155               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11156               el.fxDefaults = {stopFx: true};
11157               // maximum custom styling
11158               //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>');
11159               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>');              
11160               tipTitle = el.child('h3');
11161               tipTitle.enableDisplayMode("block");
11162               tipBody = el.child('div.x-tip-bd');
11163               tipBodyText = el.child('div.x-tip-bd-inner');
11164               //bdLeft = el.child('div.x-tip-bd-left');
11165               //bdRight = el.child('div.x-tip-bd-right');
11166               close = el.child('div.x-tip-close');
11167               close.enableDisplayMode("block");
11168               close.on("click", hide);
11169               var d = Roo.get(document);
11170               d.on("mousedown", onDown);
11171               d.on("mouseover", onOver);
11172               d.on("mouseout", onOut);
11173               d.on("mousemove", onMove);
11174               esc = d.addKeyListener(27, hide);
11175               esc.disable();
11176               if(Roo.dd.DD){
11177                   dd = el.initDD("default", null, {
11178                       onDrag : function(){
11179                           el.sync();  
11180                       }
11181                   });
11182                   dd.setHandleElId(tipTitle.id);
11183                   dd.lock();
11184               }
11185               inited = true;
11186           }
11187           this.enable(); 
11188        },
11189
11190     /**
11191      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11192      * are supported:
11193      * <pre>
11194 Property    Type                   Description
11195 ----------  ---------------------  ------------------------------------------------------------------------
11196 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11197      * </ul>
11198      * @param {Object} config The config object
11199      */
11200        register : function(config){
11201            var cs = config instanceof Array ? config : arguments;
11202            for(var i = 0, len = cs.length; i < len; i++) {
11203                var c = cs[i];
11204                var target = c.target;
11205                if(target){
11206                    if(target instanceof Array){
11207                        for(var j = 0, jlen = target.length; j < jlen; j++){
11208                            tagEls[target[j]] = c;
11209                        }
11210                    }else{
11211                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11212                    }
11213                }
11214            }
11215        },
11216
11217     /**
11218      * Removes this quick tip from its element and destroys it.
11219      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11220      */
11221        unregister : function(el){
11222            delete tagEls[Roo.id(el)];
11223        },
11224
11225     /**
11226      * Enable this quick tip.
11227      */
11228        enable : function(){
11229            if(inited && disabled){
11230                locks.pop();
11231                if(locks.length < 1){
11232                    disabled = false;
11233                }
11234            }
11235        },
11236
11237     /**
11238      * Disable this quick tip.
11239      */
11240        disable : function(){
11241           disabled = true;
11242           clearTimeout(showProc);
11243           clearTimeout(hideProc);
11244           clearTimeout(dismissProc);
11245           if(ce){
11246               hide(true);
11247           }
11248           locks.push(1);
11249        },
11250
11251     /**
11252      * Returns true if the quick tip is enabled, else false.
11253      */
11254        isEnabled : function(){
11255             return !disabled;
11256        },
11257
11258         // private
11259        tagConfig : {
11260            namespace : "roo", // was ext?? this may break..
11261            alt_namespace : "ext",
11262            attribute : "qtip",
11263            width : "width",
11264            target : "target",
11265            title : "qtitle",
11266            hide : "hide",
11267            cls : "qclass"
11268        }
11269    };
11270 }();
11271
11272 // backwards compat
11273 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11274  * Based on:
11275  * Ext JS Library 1.1.1
11276  * Copyright(c) 2006-2007, Ext JS, LLC.
11277  *
11278  * Originally Released Under LGPL - original licence link has changed is not relivant.
11279  *
11280  * Fork - LGPL
11281  * <script type="text/javascript">
11282  */
11283  
11284
11285 /**
11286  * @class Roo.tree.TreePanel
11287  * @extends Roo.data.Tree
11288  * @cfg {Roo.tree.TreeNode} root The root node
11289  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11290  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11291  * @cfg {Boolean} enableDD true to enable drag and drop
11292  * @cfg {Boolean} enableDrag true to enable just drag
11293  * @cfg {Boolean} enableDrop true to enable just drop
11294  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11295  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11296  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11297  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11298  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11299  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11300  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11301  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11302  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11303  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11304  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11305  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11306  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11307  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11308  * @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>
11309  * @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>
11310  * 
11311  * @constructor
11312  * @param {String/HTMLElement/Element} el The container element
11313  * @param {Object} config
11314  */
11315 Roo.tree.TreePanel = function(el, config){
11316     var root = false;
11317     var loader = false;
11318     if (config.root) {
11319         root = config.root;
11320         delete config.root;
11321     }
11322     if (config.loader) {
11323         loader = config.loader;
11324         delete config.loader;
11325     }
11326     
11327     Roo.apply(this, config);
11328     Roo.tree.TreePanel.superclass.constructor.call(this);
11329     this.el = Roo.get(el);
11330     this.el.addClass('x-tree');
11331     //console.log(root);
11332     if (root) {
11333         this.setRootNode( Roo.factory(root, Roo.tree));
11334     }
11335     if (loader) {
11336         this.loader = Roo.factory(loader, Roo.tree);
11337     }
11338    /**
11339     * Read-only. The id of the container element becomes this TreePanel's id.
11340     */
11341     this.id = this.el.id;
11342     this.addEvents({
11343         /**
11344         * @event beforeload
11345         * Fires before a node is loaded, return false to cancel
11346         * @param {Node} node The node being loaded
11347         */
11348         "beforeload" : true,
11349         /**
11350         * @event load
11351         * Fires when a node is loaded
11352         * @param {Node} node The node that was loaded
11353         */
11354         "load" : true,
11355         /**
11356         * @event textchange
11357         * Fires when the text for a node is changed
11358         * @param {Node} node The node
11359         * @param {String} text The new text
11360         * @param {String} oldText The old text
11361         */
11362         "textchange" : true,
11363         /**
11364         * @event beforeexpand
11365         * Fires before a node is expanded, return false to cancel.
11366         * @param {Node} node The node
11367         * @param {Boolean} deep
11368         * @param {Boolean} anim
11369         */
11370         "beforeexpand" : true,
11371         /**
11372         * @event beforecollapse
11373         * Fires before a node is collapsed, return false to cancel.
11374         * @param {Node} node The node
11375         * @param {Boolean} deep
11376         * @param {Boolean} anim
11377         */
11378         "beforecollapse" : true,
11379         /**
11380         * @event expand
11381         * Fires when a node is expanded
11382         * @param {Node} node The node
11383         */
11384         "expand" : true,
11385         /**
11386         * @event disabledchange
11387         * Fires when the disabled status of a node changes
11388         * @param {Node} node The node
11389         * @param {Boolean} disabled
11390         */
11391         "disabledchange" : true,
11392         /**
11393         * @event collapse
11394         * Fires when a node is collapsed
11395         * @param {Node} node The node
11396         */
11397         "collapse" : true,
11398         /**
11399         * @event beforeclick
11400         * Fires before click processing on a node. Return false to cancel the default action.
11401         * @param {Node} node The node
11402         * @param {Roo.EventObject} e The event object
11403         */
11404         "beforeclick":true,
11405         /**
11406         * @event checkchange
11407         * Fires when a node with a checkbox's checked property changes
11408         * @param {Node} this This node
11409         * @param {Boolean} checked
11410         */
11411         "checkchange":true,
11412         /**
11413         * @event click
11414         * Fires when a node is clicked
11415         * @param {Node} node The node
11416         * @param {Roo.EventObject} e The event object
11417         */
11418         "click":true,
11419         /**
11420         * @event dblclick
11421         * Fires when a node is double clicked
11422         * @param {Node} node The node
11423         * @param {Roo.EventObject} e The event object
11424         */
11425         "dblclick":true,
11426         /**
11427         * @event contextmenu
11428         * Fires when a node is right clicked
11429         * @param {Node} node The node
11430         * @param {Roo.EventObject} e The event object
11431         */
11432         "contextmenu":true,
11433         /**
11434         * @event beforechildrenrendered
11435         * Fires right before the child nodes for a node are rendered
11436         * @param {Node} node The node
11437         */
11438         "beforechildrenrendered":true,
11439         /**
11440         * @event startdrag
11441         * Fires when a node starts being dragged
11442         * @param {Roo.tree.TreePanel} this
11443         * @param {Roo.tree.TreeNode} node
11444         * @param {event} e The raw browser event
11445         */ 
11446        "startdrag" : true,
11447        /**
11448         * @event enddrag
11449         * Fires when a drag operation is complete
11450         * @param {Roo.tree.TreePanel} this
11451         * @param {Roo.tree.TreeNode} node
11452         * @param {event} e The raw browser event
11453         */
11454        "enddrag" : true,
11455        /**
11456         * @event dragdrop
11457         * Fires when a dragged node is dropped on a valid DD target
11458         * @param {Roo.tree.TreePanel} this
11459         * @param {Roo.tree.TreeNode} node
11460         * @param {DD} dd The dd it was dropped on
11461         * @param {event} e The raw browser event
11462         */
11463        "dragdrop" : true,
11464        /**
11465         * @event beforenodedrop
11466         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11467         * passed to handlers has the following properties:<br />
11468         * <ul style="padding:5px;padding-left:16px;">
11469         * <li>tree - The TreePanel</li>
11470         * <li>target - The node being targeted for the drop</li>
11471         * <li>data - The drag data from the drag source</li>
11472         * <li>point - The point of the drop - append, above or below</li>
11473         * <li>source - The drag source</li>
11474         * <li>rawEvent - Raw mouse event</li>
11475         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11476         * to be inserted by setting them on this object.</li>
11477         * <li>cancel - Set this to true to cancel the drop.</li>
11478         * </ul>
11479         * @param {Object} dropEvent
11480         */
11481        "beforenodedrop" : true,
11482        /**
11483         * @event nodedrop
11484         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11485         * passed to handlers has the following properties:<br />
11486         * <ul style="padding:5px;padding-left:16px;">
11487         * <li>tree - The TreePanel</li>
11488         * <li>target - The node being targeted for the drop</li>
11489         * <li>data - The drag data from the drag source</li>
11490         * <li>point - The point of the drop - append, above or below</li>
11491         * <li>source - The drag source</li>
11492         * <li>rawEvent - Raw mouse event</li>
11493         * <li>dropNode - Dropped node(s).</li>
11494         * </ul>
11495         * @param {Object} dropEvent
11496         */
11497        "nodedrop" : true,
11498         /**
11499         * @event nodedragover
11500         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11501         * passed to handlers has the following properties:<br />
11502         * <ul style="padding:5px;padding-left:16px;">
11503         * <li>tree - The TreePanel</li>
11504         * <li>target - The node being targeted for the drop</li>
11505         * <li>data - The drag data from the drag source</li>
11506         * <li>point - The point of the drop - append, above or below</li>
11507         * <li>source - The drag source</li>
11508         * <li>rawEvent - Raw mouse event</li>
11509         * <li>dropNode - Drop node(s) provided by the source.</li>
11510         * <li>cancel - Set this to true to signal drop not allowed.</li>
11511         * </ul>
11512         * @param {Object} dragOverEvent
11513         */
11514        "nodedragover" : true,
11515        /**
11516         * @event appendnode
11517         * Fires when append node to the tree
11518         * @param {Roo.tree.TreePanel} this
11519         * @param {Roo.tree.TreeNode} node
11520         * @param {Number} index The index of the newly appended node
11521         */
11522        "appendnode" : true
11523         
11524     });
11525     if(this.singleExpand){
11526        this.on("beforeexpand", this.restrictExpand, this);
11527     }
11528     if (this.editor) {
11529         this.editor.tree = this;
11530         this.editor = Roo.factory(this.editor, Roo.tree);
11531     }
11532     
11533     if (this.selModel) {
11534         this.selModel = Roo.factory(this.selModel, Roo.tree);
11535     }
11536    
11537 };
11538 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11539     rootVisible : true,
11540     animate: Roo.enableFx,
11541     lines : true,
11542     enableDD : false,
11543     hlDrop : Roo.enableFx,
11544   
11545     renderer: false,
11546     
11547     rendererTip: false,
11548     // private
11549     restrictExpand : function(node){
11550         var p = node.parentNode;
11551         if(p){
11552             if(p.expandedChild && p.expandedChild.parentNode == p){
11553                 p.expandedChild.collapse();
11554             }
11555             p.expandedChild = node;
11556         }
11557     },
11558
11559     // private override
11560     setRootNode : function(node){
11561         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11562         if(!this.rootVisible){
11563             node.ui = new Roo.tree.RootTreeNodeUI(node);
11564         }
11565         return node;
11566     },
11567
11568     /**
11569      * Returns the container element for this TreePanel
11570      */
11571     getEl : function(){
11572         return this.el;
11573     },
11574
11575     /**
11576      * Returns the default TreeLoader for this TreePanel
11577      */
11578     getLoader : function(){
11579         return this.loader;
11580     },
11581
11582     /**
11583      * Expand all nodes
11584      */
11585     expandAll : function(){
11586         this.root.expand(true);
11587     },
11588
11589     /**
11590      * Collapse all nodes
11591      */
11592     collapseAll : function(){
11593         this.root.collapse(true);
11594     },
11595
11596     /**
11597      * Returns the selection model used by this TreePanel
11598      */
11599     getSelectionModel : function(){
11600         if(!this.selModel){
11601             this.selModel = new Roo.tree.DefaultSelectionModel();
11602         }
11603         return this.selModel;
11604     },
11605
11606     /**
11607      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11608      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11609      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11610      * @return {Array}
11611      */
11612     getChecked : function(a, startNode){
11613         startNode = startNode || this.root;
11614         var r = [];
11615         var f = function(){
11616             if(this.attributes.checked){
11617                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11618             }
11619         }
11620         startNode.cascade(f);
11621         return r;
11622     },
11623
11624     /**
11625      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11626      * @param {String} path
11627      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11628      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11629      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11630      */
11631     expandPath : function(path, attr, callback){
11632         attr = attr || "id";
11633         var keys = path.split(this.pathSeparator);
11634         var curNode = this.root;
11635         if(curNode.attributes[attr] != keys[1]){ // invalid root
11636             if(callback){
11637                 callback(false, null);
11638             }
11639             return;
11640         }
11641         var index = 1;
11642         var f = function(){
11643             if(++index == keys.length){
11644                 if(callback){
11645                     callback(true, curNode);
11646                 }
11647                 return;
11648             }
11649             var c = curNode.findChild(attr, keys[index]);
11650             if(!c){
11651                 if(callback){
11652                     callback(false, curNode);
11653                 }
11654                 return;
11655             }
11656             curNode = c;
11657             c.expand(false, false, f);
11658         };
11659         curNode.expand(false, false, f);
11660     },
11661
11662     /**
11663      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11664      * @param {String} path
11665      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11666      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11667      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11668      */
11669     selectPath : function(path, attr, callback){
11670         attr = attr || "id";
11671         var keys = path.split(this.pathSeparator);
11672         var v = keys.pop();
11673         if(keys.length > 0){
11674             var f = function(success, node){
11675                 if(success && node){
11676                     var n = node.findChild(attr, v);
11677                     if(n){
11678                         n.select();
11679                         if(callback){
11680                             callback(true, n);
11681                         }
11682                     }else if(callback){
11683                         callback(false, n);
11684                     }
11685                 }else{
11686                     if(callback){
11687                         callback(false, n);
11688                     }
11689                 }
11690             };
11691             this.expandPath(keys.join(this.pathSeparator), attr, f);
11692         }else{
11693             this.root.select();
11694             if(callback){
11695                 callback(true, this.root);
11696             }
11697         }
11698     },
11699
11700     getTreeEl : function(){
11701         return this.el;
11702     },
11703
11704     /**
11705      * Trigger rendering of this TreePanel
11706      */
11707     render : function(){
11708         if (this.innerCt) {
11709             return this; // stop it rendering more than once!!
11710         }
11711         
11712         this.innerCt = this.el.createChild({tag:"ul",
11713                cls:"x-tree-root-ct " +
11714                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11715
11716         if(this.containerScroll){
11717             Roo.dd.ScrollManager.register(this.el);
11718         }
11719         if((this.enableDD || this.enableDrop) && !this.dropZone){
11720            /**
11721             * The dropZone used by this tree if drop is enabled
11722             * @type Roo.tree.TreeDropZone
11723             */
11724              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11725                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11726            });
11727         }
11728         if((this.enableDD || this.enableDrag) && !this.dragZone){
11729            /**
11730             * The dragZone used by this tree if drag is enabled
11731             * @type Roo.tree.TreeDragZone
11732             */
11733             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11734                ddGroup: this.ddGroup || "TreeDD",
11735                scroll: this.ddScroll
11736            });
11737         }
11738         this.getSelectionModel().init(this);
11739         if (!this.root) {
11740             Roo.log("ROOT not set in tree");
11741             return this;
11742         }
11743         this.root.render();
11744         if(!this.rootVisible){
11745             this.root.renderChildren();
11746         }
11747         return this;
11748     }
11749 });/*
11750  * Based on:
11751  * Ext JS Library 1.1.1
11752  * Copyright(c) 2006-2007, Ext JS, LLC.
11753  *
11754  * Originally Released Under LGPL - original licence link has changed is not relivant.
11755  *
11756  * Fork - LGPL
11757  * <script type="text/javascript">
11758  */
11759  
11760
11761 /**
11762  * @class Roo.tree.DefaultSelectionModel
11763  * @extends Roo.util.Observable
11764  * The default single selection for a TreePanel.
11765  * @param {Object} cfg Configuration
11766  */
11767 Roo.tree.DefaultSelectionModel = function(cfg){
11768    this.selNode = null;
11769    
11770    
11771    
11772    this.addEvents({
11773        /**
11774         * @event selectionchange
11775         * Fires when the selected node changes
11776         * @param {DefaultSelectionModel} this
11777         * @param {TreeNode} node the new selection
11778         */
11779        "selectionchange" : true,
11780
11781        /**
11782         * @event beforeselect
11783         * Fires before the selected node changes, return false to cancel the change
11784         * @param {DefaultSelectionModel} this
11785         * @param {TreeNode} node the new selection
11786         * @param {TreeNode} node the old selection
11787         */
11788        "beforeselect" : true
11789    });
11790    
11791     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11792 };
11793
11794 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11795     init : function(tree){
11796         this.tree = tree;
11797         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11798         tree.on("click", this.onNodeClick, this);
11799     },
11800     
11801     onNodeClick : function(node, e){
11802         if (e.ctrlKey && this.selNode == node)  {
11803             this.unselect(node);
11804             return;
11805         }
11806         this.select(node);
11807     },
11808     
11809     /**
11810      * Select a node.
11811      * @param {TreeNode} node The node to select
11812      * @return {TreeNode} The selected node
11813      */
11814     select : function(node){
11815         var last = this.selNode;
11816         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11817             if(last){
11818                 last.ui.onSelectedChange(false);
11819             }
11820             this.selNode = node;
11821             node.ui.onSelectedChange(true);
11822             this.fireEvent("selectionchange", this, node, last);
11823         }
11824         return node;
11825     },
11826     
11827     /**
11828      * Deselect a node.
11829      * @param {TreeNode} node The node to unselect
11830      */
11831     unselect : function(node){
11832         if(this.selNode == node){
11833             this.clearSelections();
11834         }    
11835     },
11836     
11837     /**
11838      * Clear all selections
11839      */
11840     clearSelections : function(){
11841         var n = this.selNode;
11842         if(n){
11843             n.ui.onSelectedChange(false);
11844             this.selNode = null;
11845             this.fireEvent("selectionchange", this, null);
11846         }
11847         return n;
11848     },
11849     
11850     /**
11851      * Get the selected node
11852      * @return {TreeNode} The selected node
11853      */
11854     getSelectedNode : function(){
11855         return this.selNode;    
11856     },
11857     
11858     /**
11859      * Returns true if the node is selected
11860      * @param {TreeNode} node The node to check
11861      * @return {Boolean}
11862      */
11863     isSelected : function(node){
11864         return this.selNode == node;  
11865     },
11866
11867     /**
11868      * Selects the node above the selected node in the tree, intelligently walking the nodes
11869      * @return TreeNode The new selection
11870      */
11871     selectPrevious : function(){
11872         var s = this.selNode || this.lastSelNode;
11873         if(!s){
11874             return null;
11875         }
11876         var ps = s.previousSibling;
11877         if(ps){
11878             if(!ps.isExpanded() || ps.childNodes.length < 1){
11879                 return this.select(ps);
11880             } else{
11881                 var lc = ps.lastChild;
11882                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11883                     lc = lc.lastChild;
11884                 }
11885                 return this.select(lc);
11886             }
11887         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11888             return this.select(s.parentNode);
11889         }
11890         return null;
11891     },
11892
11893     /**
11894      * Selects the node above the selected node in the tree, intelligently walking the nodes
11895      * @return TreeNode The new selection
11896      */
11897     selectNext : function(){
11898         var s = this.selNode || this.lastSelNode;
11899         if(!s){
11900             return null;
11901         }
11902         if(s.firstChild && s.isExpanded()){
11903              return this.select(s.firstChild);
11904          }else if(s.nextSibling){
11905              return this.select(s.nextSibling);
11906          }else if(s.parentNode){
11907             var newS = null;
11908             s.parentNode.bubble(function(){
11909                 if(this.nextSibling){
11910                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11911                     return false;
11912                 }
11913             });
11914             return newS;
11915          }
11916         return null;
11917     },
11918
11919     onKeyDown : function(e){
11920         var s = this.selNode || this.lastSelNode;
11921         // undesirable, but required
11922         var sm = this;
11923         if(!s){
11924             return;
11925         }
11926         var k = e.getKey();
11927         switch(k){
11928              case e.DOWN:
11929                  e.stopEvent();
11930                  this.selectNext();
11931              break;
11932              case e.UP:
11933                  e.stopEvent();
11934                  this.selectPrevious();
11935              break;
11936              case e.RIGHT:
11937                  e.preventDefault();
11938                  if(s.hasChildNodes()){
11939                      if(!s.isExpanded()){
11940                          s.expand();
11941                      }else if(s.firstChild){
11942                          this.select(s.firstChild, e);
11943                      }
11944                  }
11945              break;
11946              case e.LEFT:
11947                  e.preventDefault();
11948                  if(s.hasChildNodes() && s.isExpanded()){
11949                      s.collapse();
11950                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11951                      this.select(s.parentNode, e);
11952                  }
11953              break;
11954         };
11955     }
11956 });
11957
11958 /**
11959  * @class Roo.tree.MultiSelectionModel
11960  * @extends Roo.util.Observable
11961  * Multi selection for a TreePanel.
11962  * @param {Object} cfg Configuration
11963  */
11964 Roo.tree.MultiSelectionModel = function(){
11965    this.selNodes = [];
11966    this.selMap = {};
11967    this.addEvents({
11968        /**
11969         * @event selectionchange
11970         * Fires when the selected nodes change
11971         * @param {MultiSelectionModel} this
11972         * @param {Array} nodes Array of the selected nodes
11973         */
11974        "selectionchange" : true
11975    });
11976    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11977    
11978 };
11979
11980 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11981     init : function(tree){
11982         this.tree = tree;
11983         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11984         tree.on("click", this.onNodeClick, this);
11985     },
11986     
11987     onNodeClick : function(node, e){
11988         this.select(node, e, e.ctrlKey);
11989     },
11990     
11991     /**
11992      * Select a node.
11993      * @param {TreeNode} node The node to select
11994      * @param {EventObject} e (optional) An event associated with the selection
11995      * @param {Boolean} keepExisting True to retain existing selections
11996      * @return {TreeNode} The selected node
11997      */
11998     select : function(node, e, keepExisting){
11999         if(keepExisting !== true){
12000             this.clearSelections(true);
12001         }
12002         if(this.isSelected(node)){
12003             this.lastSelNode = node;
12004             return node;
12005         }
12006         this.selNodes.push(node);
12007         this.selMap[node.id] = node;
12008         this.lastSelNode = node;
12009         node.ui.onSelectedChange(true);
12010         this.fireEvent("selectionchange", this, this.selNodes);
12011         return node;
12012     },
12013     
12014     /**
12015      * Deselect a node.
12016      * @param {TreeNode} node The node to unselect
12017      */
12018     unselect : function(node){
12019         if(this.selMap[node.id]){
12020             node.ui.onSelectedChange(false);
12021             var sn = this.selNodes;
12022             var index = -1;
12023             if(sn.indexOf){
12024                 index = sn.indexOf(node);
12025             }else{
12026                 for(var i = 0, len = sn.length; i < len; i++){
12027                     if(sn[i] == node){
12028                         index = i;
12029                         break;
12030                     }
12031                 }
12032             }
12033             if(index != -1){
12034                 this.selNodes.splice(index, 1);
12035             }
12036             delete this.selMap[node.id];
12037             this.fireEvent("selectionchange", this, this.selNodes);
12038         }
12039     },
12040     
12041     /**
12042      * Clear all selections
12043      */
12044     clearSelections : function(suppressEvent){
12045         var sn = this.selNodes;
12046         if(sn.length > 0){
12047             for(var i = 0, len = sn.length; i < len; i++){
12048                 sn[i].ui.onSelectedChange(false);
12049             }
12050             this.selNodes = [];
12051             this.selMap = {};
12052             if(suppressEvent !== true){
12053                 this.fireEvent("selectionchange", this, this.selNodes);
12054             }
12055         }
12056     },
12057     
12058     /**
12059      * Returns true if the node is selected
12060      * @param {TreeNode} node The node to check
12061      * @return {Boolean}
12062      */
12063     isSelected : function(node){
12064         return this.selMap[node.id] ? true : false;  
12065     },
12066     
12067     /**
12068      * Returns an array of the selected nodes
12069      * @return {Array}
12070      */
12071     getSelectedNodes : function(){
12072         return this.selNodes;    
12073     },
12074
12075     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12076
12077     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12078
12079     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12080 });/*
12081  * Based on:
12082  * Ext JS Library 1.1.1
12083  * Copyright(c) 2006-2007, Ext JS, LLC.
12084  *
12085  * Originally Released Under LGPL - original licence link has changed is not relivant.
12086  *
12087  * Fork - LGPL
12088  * <script type="text/javascript">
12089  */
12090  
12091 /**
12092  * @class Roo.tree.TreeNode
12093  * @extends Roo.data.Node
12094  * @cfg {String} text The text for this node
12095  * @cfg {Boolean} expanded true to start the node expanded
12096  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12097  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12098  * @cfg {Boolean} disabled true to start the node disabled
12099  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12100  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12101  * @cfg {String} cls A css class to be added to the node
12102  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12103  * @cfg {String} href URL of the link used for the node (defaults to #)
12104  * @cfg {String} hrefTarget target frame for the link
12105  * @cfg {String} qtip An Ext QuickTip for the node
12106  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12107  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12108  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12109  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12110  * (defaults to undefined with no checkbox rendered)
12111  * @constructor
12112  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12113  */
12114 Roo.tree.TreeNode = function(attributes){
12115     attributes = attributes || {};
12116     if(typeof attributes == "string"){
12117         attributes = {text: attributes};
12118     }
12119     this.childrenRendered = false;
12120     this.rendered = false;
12121     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12122     this.expanded = attributes.expanded === true;
12123     this.isTarget = attributes.isTarget !== false;
12124     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12125     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12126
12127     /**
12128      * Read-only. The text for this node. To change it use setText().
12129      * @type String
12130      */
12131     this.text = attributes.text;
12132     /**
12133      * True if this node is disabled.
12134      * @type Boolean
12135      */
12136     this.disabled = attributes.disabled === true;
12137
12138     this.addEvents({
12139         /**
12140         * @event textchange
12141         * Fires when the text for this node is changed
12142         * @param {Node} this This node
12143         * @param {String} text The new text
12144         * @param {String} oldText The old text
12145         */
12146         "textchange" : true,
12147         /**
12148         * @event beforeexpand
12149         * Fires before this node is expanded, return false to cancel.
12150         * @param {Node} this This node
12151         * @param {Boolean} deep
12152         * @param {Boolean} anim
12153         */
12154         "beforeexpand" : true,
12155         /**
12156         * @event beforecollapse
12157         * Fires before this node is collapsed, return false to cancel.
12158         * @param {Node} this This node
12159         * @param {Boolean} deep
12160         * @param {Boolean} anim
12161         */
12162         "beforecollapse" : true,
12163         /**
12164         * @event expand
12165         * Fires when this node is expanded
12166         * @param {Node} this This node
12167         */
12168         "expand" : true,
12169         /**
12170         * @event disabledchange
12171         * Fires when the disabled status of this node changes
12172         * @param {Node} this This node
12173         * @param {Boolean} disabled
12174         */
12175         "disabledchange" : true,
12176         /**
12177         * @event collapse
12178         * Fires when this node is collapsed
12179         * @param {Node} this This node
12180         */
12181         "collapse" : true,
12182         /**
12183         * @event beforeclick
12184         * Fires before click processing. Return false to cancel the default action.
12185         * @param {Node} this This node
12186         * @param {Roo.EventObject} e The event object
12187         */
12188         "beforeclick":true,
12189         /**
12190         * @event checkchange
12191         * Fires when a node with a checkbox's checked property changes
12192         * @param {Node} this This node
12193         * @param {Boolean} checked
12194         */
12195         "checkchange":true,
12196         /**
12197         * @event click
12198         * Fires when this node is clicked
12199         * @param {Node} this This node
12200         * @param {Roo.EventObject} e The event object
12201         */
12202         "click":true,
12203         /**
12204         * @event dblclick
12205         * Fires when this node is double clicked
12206         * @param {Node} this This node
12207         * @param {Roo.EventObject} e The event object
12208         */
12209         "dblclick":true,
12210         /**
12211         * @event contextmenu
12212         * Fires when this node is right clicked
12213         * @param {Node} this This node
12214         * @param {Roo.EventObject} e The event object
12215         */
12216         "contextmenu":true,
12217         /**
12218         * @event beforechildrenrendered
12219         * Fires right before the child nodes for this node are rendered
12220         * @param {Node} this This node
12221         */
12222         "beforechildrenrendered":true
12223     });
12224
12225     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12226
12227     /**
12228      * Read-only. The UI for this node
12229      * @type TreeNodeUI
12230      */
12231     this.ui = new uiClass(this);
12232     
12233     // finally support items[]
12234     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12235         return;
12236     }
12237     
12238     
12239     Roo.each(this.attributes.items, function(c) {
12240         this.appendChild(Roo.factory(c,Roo.Tree));
12241     }, this);
12242     delete this.attributes.items;
12243     
12244     
12245     
12246 };
12247 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12248     preventHScroll: true,
12249     /**
12250      * Returns true if this node is expanded
12251      * @return {Boolean}
12252      */
12253     isExpanded : function(){
12254         return this.expanded;
12255     },
12256
12257     /**
12258      * Returns the UI object for this node
12259      * @return {TreeNodeUI}
12260      */
12261     getUI : function(){
12262         return this.ui;
12263     },
12264
12265     // private override
12266     setFirstChild : function(node){
12267         var of = this.firstChild;
12268         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12269         if(this.childrenRendered && of && node != of){
12270             of.renderIndent(true, true);
12271         }
12272         if(this.rendered){
12273             this.renderIndent(true, true);
12274         }
12275     },
12276
12277     // private override
12278     setLastChild : function(node){
12279         var ol = this.lastChild;
12280         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12281         if(this.childrenRendered && ol && node != ol){
12282             ol.renderIndent(true, true);
12283         }
12284         if(this.rendered){
12285             this.renderIndent(true, true);
12286         }
12287     },
12288
12289     // these methods are overridden to provide lazy rendering support
12290     // private override
12291     appendChild : function()
12292     {
12293         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12294         if(node && this.childrenRendered){
12295             node.render();
12296         }
12297         this.ui.updateExpandIcon();
12298         return node;
12299     },
12300
12301     // private override
12302     removeChild : function(node){
12303         this.ownerTree.getSelectionModel().unselect(node);
12304         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12305         // if it's been rendered remove dom node
12306         if(this.childrenRendered){
12307             node.ui.remove();
12308         }
12309         if(this.childNodes.length < 1){
12310             this.collapse(false, false);
12311         }else{
12312             this.ui.updateExpandIcon();
12313         }
12314         if(!this.firstChild) {
12315             this.childrenRendered = false;
12316         }
12317         return node;
12318     },
12319
12320     // private override
12321     insertBefore : function(node, refNode){
12322         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12323         if(newNode && refNode && this.childrenRendered){
12324             node.render();
12325         }
12326         this.ui.updateExpandIcon();
12327         return newNode;
12328     },
12329
12330     /**
12331      * Sets the text for this node
12332      * @param {String} text
12333      */
12334     setText : function(text){
12335         var oldText = this.text;
12336         this.text = text;
12337         this.attributes.text = text;
12338         if(this.rendered){ // event without subscribing
12339             this.ui.onTextChange(this, text, oldText);
12340         }
12341         this.fireEvent("textchange", this, text, oldText);
12342     },
12343
12344     /**
12345      * Triggers selection of this node
12346      */
12347     select : function(){
12348         this.getOwnerTree().getSelectionModel().select(this);
12349     },
12350
12351     /**
12352      * Triggers deselection of this node
12353      */
12354     unselect : function(){
12355         this.getOwnerTree().getSelectionModel().unselect(this);
12356     },
12357
12358     /**
12359      * Returns true if this node is selected
12360      * @return {Boolean}
12361      */
12362     isSelected : function(){
12363         return this.getOwnerTree().getSelectionModel().isSelected(this);
12364     },
12365
12366     /**
12367      * Expand this node.
12368      * @param {Boolean} deep (optional) True to expand all children as well
12369      * @param {Boolean} anim (optional) false to cancel the default animation
12370      * @param {Function} callback (optional) A callback to be called when
12371      * expanding this node completes (does not wait for deep expand to complete).
12372      * Called with 1 parameter, this node.
12373      */
12374     expand : function(deep, anim, callback){
12375         if(!this.expanded){
12376             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12377                 return;
12378             }
12379             if(!this.childrenRendered){
12380                 this.renderChildren();
12381             }
12382             this.expanded = true;
12383             
12384             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12385                 this.ui.animExpand(function(){
12386                     this.fireEvent("expand", this);
12387                     if(typeof callback == "function"){
12388                         callback(this);
12389                     }
12390                     if(deep === true){
12391                         this.expandChildNodes(true);
12392                     }
12393                 }.createDelegate(this));
12394                 return;
12395             }else{
12396                 this.ui.expand();
12397                 this.fireEvent("expand", this);
12398                 if(typeof callback == "function"){
12399                     callback(this);
12400                 }
12401             }
12402         }else{
12403            if(typeof callback == "function"){
12404                callback(this);
12405            }
12406         }
12407         if(deep === true){
12408             this.expandChildNodes(true);
12409         }
12410     },
12411
12412     isHiddenRoot : function(){
12413         return this.isRoot && !this.getOwnerTree().rootVisible;
12414     },
12415
12416     /**
12417      * Collapse this node.
12418      * @param {Boolean} deep (optional) True to collapse all children as well
12419      * @param {Boolean} anim (optional) false to cancel the default animation
12420      */
12421     collapse : function(deep, anim){
12422         if(this.expanded && !this.isHiddenRoot()){
12423             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12424                 return;
12425             }
12426             this.expanded = false;
12427             if((this.getOwnerTree().animate && anim !== false) || anim){
12428                 this.ui.animCollapse(function(){
12429                     this.fireEvent("collapse", this);
12430                     if(deep === true){
12431                         this.collapseChildNodes(true);
12432                     }
12433                 }.createDelegate(this));
12434                 return;
12435             }else{
12436                 this.ui.collapse();
12437                 this.fireEvent("collapse", this);
12438             }
12439         }
12440         if(deep === true){
12441             var cs = this.childNodes;
12442             for(var i = 0, len = cs.length; i < len; i++) {
12443                 cs[i].collapse(true, false);
12444             }
12445         }
12446     },
12447
12448     // private
12449     delayedExpand : function(delay){
12450         if(!this.expandProcId){
12451             this.expandProcId = this.expand.defer(delay, this);
12452         }
12453     },
12454
12455     // private
12456     cancelExpand : function(){
12457         if(this.expandProcId){
12458             clearTimeout(this.expandProcId);
12459         }
12460         this.expandProcId = false;
12461     },
12462
12463     /**
12464      * Toggles expanded/collapsed state of the node
12465      */
12466     toggle : function(){
12467         if(this.expanded){
12468             this.collapse();
12469         }else{
12470             this.expand();
12471         }
12472     },
12473
12474     /**
12475      * Ensures all parent nodes are expanded
12476      */
12477     ensureVisible : function(callback){
12478         var tree = this.getOwnerTree();
12479         tree.expandPath(this.parentNode.getPath(), false, function(){
12480             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12481             Roo.callback(callback);
12482         }.createDelegate(this));
12483     },
12484
12485     /**
12486      * Expand all child nodes
12487      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12488      */
12489     expandChildNodes : function(deep){
12490         var cs = this.childNodes;
12491         for(var i = 0, len = cs.length; i < len; i++) {
12492                 cs[i].expand(deep);
12493         }
12494     },
12495
12496     /**
12497      * Collapse all child nodes
12498      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12499      */
12500     collapseChildNodes : function(deep){
12501         var cs = this.childNodes;
12502         for(var i = 0, len = cs.length; i < len; i++) {
12503                 cs[i].collapse(deep);
12504         }
12505     },
12506
12507     /**
12508      * Disables this node
12509      */
12510     disable : function(){
12511         this.disabled = true;
12512         this.unselect();
12513         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12514             this.ui.onDisableChange(this, true);
12515         }
12516         this.fireEvent("disabledchange", this, true);
12517     },
12518
12519     /**
12520      * Enables this node
12521      */
12522     enable : function(){
12523         this.disabled = false;
12524         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12525             this.ui.onDisableChange(this, false);
12526         }
12527         this.fireEvent("disabledchange", this, false);
12528     },
12529
12530     // private
12531     renderChildren : function(suppressEvent){
12532         if(suppressEvent !== false){
12533             this.fireEvent("beforechildrenrendered", this);
12534         }
12535         var cs = this.childNodes;
12536         for(var i = 0, len = cs.length; i < len; i++){
12537             cs[i].render(true);
12538         }
12539         this.childrenRendered = true;
12540     },
12541
12542     // private
12543     sort : function(fn, scope){
12544         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12545         if(this.childrenRendered){
12546             var cs = this.childNodes;
12547             for(var i = 0, len = cs.length; i < len; i++){
12548                 cs[i].render(true);
12549             }
12550         }
12551     },
12552
12553     // private
12554     render : function(bulkRender){
12555         this.ui.render(bulkRender);
12556         if(!this.rendered){
12557             this.rendered = true;
12558             if(this.expanded){
12559                 this.expanded = false;
12560                 this.expand(false, false);
12561             }
12562         }
12563     },
12564
12565     // private
12566     renderIndent : function(deep, refresh){
12567         if(refresh){
12568             this.ui.childIndent = null;
12569         }
12570         this.ui.renderIndent();
12571         if(deep === true && this.childrenRendered){
12572             var cs = this.childNodes;
12573             for(var i = 0, len = cs.length; i < len; i++){
12574                 cs[i].renderIndent(true, refresh);
12575             }
12576         }
12577     }
12578 });/*
12579  * Based on:
12580  * Ext JS Library 1.1.1
12581  * Copyright(c) 2006-2007, Ext JS, LLC.
12582  *
12583  * Originally Released Under LGPL - original licence link has changed is not relivant.
12584  *
12585  * Fork - LGPL
12586  * <script type="text/javascript">
12587  */
12588  
12589 /**
12590  * @class Roo.tree.AsyncTreeNode
12591  * @extends Roo.tree.TreeNode
12592  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12593  * @constructor
12594  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12595  */
12596  Roo.tree.AsyncTreeNode = function(config){
12597     this.loaded = false;
12598     this.loading = false;
12599     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12600     /**
12601     * @event beforeload
12602     * Fires before this node is loaded, return false to cancel
12603     * @param {Node} this This node
12604     */
12605     this.addEvents({'beforeload':true, 'load': true});
12606     /**
12607     * @event load
12608     * Fires when this node is loaded
12609     * @param {Node} this This node
12610     */
12611     /**
12612      * The loader used by this node (defaults to using the tree's defined loader)
12613      * @type TreeLoader
12614      * @property loader
12615      */
12616 };
12617 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12618     expand : function(deep, anim, callback){
12619         if(this.loading){ // if an async load is already running, waiting til it's done
12620             var timer;
12621             var f = function(){
12622                 if(!this.loading){ // done loading
12623                     clearInterval(timer);
12624                     this.expand(deep, anim, callback);
12625                 }
12626             }.createDelegate(this);
12627             timer = setInterval(f, 200);
12628             return;
12629         }
12630         if(!this.loaded){
12631             if(this.fireEvent("beforeload", this) === false){
12632                 return;
12633             }
12634             this.loading = true;
12635             this.ui.beforeLoad(this);
12636             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12637             if(loader){
12638                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12639                 return;
12640             }
12641         }
12642         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12643     },
12644     
12645     /**
12646      * Returns true if this node is currently loading
12647      * @return {Boolean}
12648      */
12649     isLoading : function(){
12650         return this.loading;  
12651     },
12652     
12653     loadComplete : function(deep, anim, callback){
12654         this.loading = false;
12655         this.loaded = true;
12656         this.ui.afterLoad(this);
12657         this.fireEvent("load", this);
12658         this.expand(deep, anim, callback);
12659     },
12660     
12661     /**
12662      * Returns true if this node has been loaded
12663      * @return {Boolean}
12664      */
12665     isLoaded : function(){
12666         return this.loaded;
12667     },
12668     
12669     hasChildNodes : function(){
12670         if(!this.isLeaf() && !this.loaded){
12671             return true;
12672         }else{
12673             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12674         }
12675     },
12676
12677     /**
12678      * Trigger a reload for this node
12679      * @param {Function} callback
12680      */
12681     reload : function(callback){
12682         this.collapse(false, false);
12683         while(this.firstChild){
12684             this.removeChild(this.firstChild);
12685         }
12686         this.childrenRendered = false;
12687         this.loaded = false;
12688         if(this.isHiddenRoot()){
12689             this.expanded = false;
12690         }
12691         this.expand(false, false, callback);
12692     }
12693 });/*
12694  * Based on:
12695  * Ext JS Library 1.1.1
12696  * Copyright(c) 2006-2007, Ext JS, LLC.
12697  *
12698  * Originally Released Under LGPL - original licence link has changed is not relivant.
12699  *
12700  * Fork - LGPL
12701  * <script type="text/javascript">
12702  */
12703  
12704 /**
12705  * @class Roo.tree.TreeNodeUI
12706  * @constructor
12707  * @param {Object} node The node to render
12708  * The TreeNode UI implementation is separate from the
12709  * tree implementation. Unless you are customizing the tree UI,
12710  * you should never have to use this directly.
12711  */
12712 Roo.tree.TreeNodeUI = function(node){
12713     this.node = node;
12714     this.rendered = false;
12715     this.animating = false;
12716     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12717 };
12718
12719 Roo.tree.TreeNodeUI.prototype = {
12720     removeChild : function(node){
12721         if(this.rendered){
12722             this.ctNode.removeChild(node.ui.getEl());
12723         }
12724     },
12725
12726     beforeLoad : function(){
12727          this.addClass("x-tree-node-loading");
12728     },
12729
12730     afterLoad : function(){
12731          this.removeClass("x-tree-node-loading");
12732     },
12733
12734     onTextChange : function(node, text, oldText){
12735         if(this.rendered){
12736             this.textNode.innerHTML = text;
12737         }
12738     },
12739
12740     onDisableChange : function(node, state){
12741         this.disabled = state;
12742         if(state){
12743             this.addClass("x-tree-node-disabled");
12744         }else{
12745             this.removeClass("x-tree-node-disabled");
12746         }
12747     },
12748
12749     onSelectedChange : function(state){
12750         if(state){
12751             this.focus();
12752             this.addClass("x-tree-selected");
12753         }else{
12754             //this.blur();
12755             this.removeClass("x-tree-selected");
12756         }
12757     },
12758
12759     onMove : function(tree, node, oldParent, newParent, index, refNode){
12760         this.childIndent = null;
12761         if(this.rendered){
12762             var targetNode = newParent.ui.getContainer();
12763             if(!targetNode){//target not rendered
12764                 this.holder = document.createElement("div");
12765                 this.holder.appendChild(this.wrap);
12766                 return;
12767             }
12768             var insertBefore = refNode ? refNode.ui.getEl() : null;
12769             if(insertBefore){
12770                 targetNode.insertBefore(this.wrap, insertBefore);
12771             }else{
12772                 targetNode.appendChild(this.wrap);
12773             }
12774             this.node.renderIndent(true);
12775         }
12776     },
12777
12778     addClass : function(cls){
12779         if(this.elNode){
12780             Roo.fly(this.elNode).addClass(cls);
12781         }
12782     },
12783
12784     removeClass : function(cls){
12785         if(this.elNode){
12786             Roo.fly(this.elNode).removeClass(cls);
12787         }
12788     },
12789
12790     remove : function(){
12791         if(this.rendered){
12792             this.holder = document.createElement("div");
12793             this.holder.appendChild(this.wrap);
12794         }
12795     },
12796
12797     fireEvent : function(){
12798         return this.node.fireEvent.apply(this.node, arguments);
12799     },
12800
12801     initEvents : function(){
12802         this.node.on("move", this.onMove, this);
12803         var E = Roo.EventManager;
12804         var a = this.anchor;
12805
12806         var el = Roo.fly(a, '_treeui');
12807
12808         if(Roo.isOpera){ // opera render bug ignores the CSS
12809             el.setStyle("text-decoration", "none");
12810         }
12811
12812         el.on("click", this.onClick, this);
12813         el.on("dblclick", this.onDblClick, this);
12814
12815         if(this.checkbox){
12816             Roo.EventManager.on(this.checkbox,
12817                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12818         }
12819
12820         el.on("contextmenu", this.onContextMenu, this);
12821
12822         var icon = Roo.fly(this.iconNode);
12823         icon.on("click", this.onClick, this);
12824         icon.on("dblclick", this.onDblClick, this);
12825         icon.on("contextmenu", this.onContextMenu, this);
12826         E.on(this.ecNode, "click", this.ecClick, this, true);
12827
12828         if(this.node.disabled){
12829             this.addClass("x-tree-node-disabled");
12830         }
12831         if(this.node.hidden){
12832             this.addClass("x-tree-node-disabled");
12833         }
12834         var ot = this.node.getOwnerTree();
12835         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12836         if(dd && (!this.node.isRoot || ot.rootVisible)){
12837             Roo.dd.Registry.register(this.elNode, {
12838                 node: this.node,
12839                 handles: this.getDDHandles(),
12840                 isHandle: false
12841             });
12842         }
12843     },
12844
12845     getDDHandles : function(){
12846         return [this.iconNode, this.textNode];
12847     },
12848
12849     hide : function(){
12850         if(this.rendered){
12851             this.wrap.style.display = "none";
12852         }
12853     },
12854
12855     show : function(){
12856         if(this.rendered){
12857             this.wrap.style.display = "";
12858         }
12859     },
12860
12861     onContextMenu : function(e){
12862         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12863             e.preventDefault();
12864             this.focus();
12865             this.fireEvent("contextmenu", this.node, e);
12866         }
12867     },
12868
12869     onClick : function(e){
12870         if(this.dropping){
12871             e.stopEvent();
12872             return;
12873         }
12874         if(this.fireEvent("beforeclick", this.node, e) !== false){
12875             if(!this.disabled && this.node.attributes.href){
12876                 this.fireEvent("click", this.node, e);
12877                 return;
12878             }
12879             e.preventDefault();
12880             if(this.disabled){
12881                 return;
12882             }
12883
12884             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12885                 this.node.toggle();
12886             }
12887
12888             this.fireEvent("click", this.node, e);
12889         }else{
12890             e.stopEvent();
12891         }
12892     },
12893
12894     onDblClick : function(e){
12895         e.preventDefault();
12896         if(this.disabled){
12897             return;
12898         }
12899         if(this.checkbox){
12900             this.toggleCheck();
12901         }
12902         if(!this.animating && this.node.hasChildNodes()){
12903             this.node.toggle();
12904         }
12905         this.fireEvent("dblclick", this.node, e);
12906     },
12907
12908     onCheckChange : function(){
12909         var checked = this.checkbox.checked;
12910         this.node.attributes.checked = checked;
12911         this.fireEvent('checkchange', this.node, checked);
12912     },
12913
12914     ecClick : function(e){
12915         if(!this.animating && this.node.hasChildNodes()){
12916             this.node.toggle();
12917         }
12918     },
12919
12920     startDrop : function(){
12921         this.dropping = true;
12922     },
12923
12924     // delayed drop so the click event doesn't get fired on a drop
12925     endDrop : function(){
12926        setTimeout(function(){
12927            this.dropping = false;
12928        }.createDelegate(this), 50);
12929     },
12930
12931     expand : function(){
12932         this.updateExpandIcon();
12933         this.ctNode.style.display = "";
12934     },
12935
12936     focus : function(){
12937         if(!this.node.preventHScroll){
12938             try{this.anchor.focus();
12939             }catch(e){}
12940         }else if(!Roo.isIE){
12941             try{
12942                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12943                 var l = noscroll.scrollLeft;
12944                 this.anchor.focus();
12945                 noscroll.scrollLeft = l;
12946             }catch(e){}
12947         }
12948     },
12949
12950     toggleCheck : function(value){
12951         var cb = this.checkbox;
12952         if(cb){
12953             cb.checked = (value === undefined ? !cb.checked : value);
12954         }
12955     },
12956
12957     blur : function(){
12958         try{
12959             this.anchor.blur();
12960         }catch(e){}
12961     },
12962
12963     animExpand : function(callback){
12964         var ct = Roo.get(this.ctNode);
12965         ct.stopFx();
12966         if(!this.node.hasChildNodes()){
12967             this.updateExpandIcon();
12968             this.ctNode.style.display = "";
12969             Roo.callback(callback);
12970             return;
12971         }
12972         this.animating = true;
12973         this.updateExpandIcon();
12974
12975         ct.slideIn('t', {
12976            callback : function(){
12977                this.animating = false;
12978                Roo.callback(callback);
12979             },
12980             scope: this,
12981             duration: this.node.ownerTree.duration || .25
12982         });
12983     },
12984
12985     highlight : function(){
12986         var tree = this.node.getOwnerTree();
12987         Roo.fly(this.wrap).highlight(
12988             tree.hlColor || "C3DAF9",
12989             {endColor: tree.hlBaseColor}
12990         );
12991     },
12992
12993     collapse : function(){
12994         this.updateExpandIcon();
12995         this.ctNode.style.display = "none";
12996     },
12997
12998     animCollapse : function(callback){
12999         var ct = Roo.get(this.ctNode);
13000         ct.enableDisplayMode('block');
13001         ct.stopFx();
13002
13003         this.animating = true;
13004         this.updateExpandIcon();
13005
13006         ct.slideOut('t', {
13007             callback : function(){
13008                this.animating = false;
13009                Roo.callback(callback);
13010             },
13011             scope: this,
13012             duration: this.node.ownerTree.duration || .25
13013         });
13014     },
13015
13016     getContainer : function(){
13017         return this.ctNode;
13018     },
13019
13020     getEl : function(){
13021         return this.wrap;
13022     },
13023
13024     appendDDGhost : function(ghostNode){
13025         ghostNode.appendChild(this.elNode.cloneNode(true));
13026     },
13027
13028     getDDRepairXY : function(){
13029         return Roo.lib.Dom.getXY(this.iconNode);
13030     },
13031
13032     onRender : function(){
13033         this.render();
13034     },
13035
13036     render : function(bulkRender){
13037         var n = this.node, a = n.attributes;
13038         var targetNode = n.parentNode ?
13039               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13040
13041         if(!this.rendered){
13042             this.rendered = true;
13043
13044             this.renderElements(n, a, targetNode, bulkRender);
13045
13046             if(a.qtip){
13047                if(this.textNode.setAttributeNS){
13048                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13049                    if(a.qtipTitle){
13050                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13051                    }
13052                }else{
13053                    this.textNode.setAttribute("ext:qtip", a.qtip);
13054                    if(a.qtipTitle){
13055                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13056                    }
13057                }
13058             }else if(a.qtipCfg){
13059                 a.qtipCfg.target = Roo.id(this.textNode);
13060                 Roo.QuickTips.register(a.qtipCfg);
13061             }
13062             this.initEvents();
13063             if(!this.node.expanded){
13064                 this.updateExpandIcon();
13065             }
13066         }else{
13067             if(bulkRender === true) {
13068                 targetNode.appendChild(this.wrap);
13069             }
13070         }
13071     },
13072
13073     renderElements : function(n, a, targetNode, bulkRender)
13074     {
13075         // add some indent caching, this helps performance when rendering a large tree
13076         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13077         var t = n.getOwnerTree();
13078         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13079         if (typeof(n.attributes.html) != 'undefined') {
13080             txt = n.attributes.html;
13081         }
13082         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13083         var cb = typeof a.checked == 'boolean';
13084         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13085         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13086             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13087             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13088             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13089             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13090             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13091              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13092                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13093             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13094             "</li>"];
13095
13096         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13097             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13098                                 n.nextSibling.ui.getEl(), buf.join(""));
13099         }else{
13100             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13101         }
13102
13103         this.elNode = this.wrap.childNodes[0];
13104         this.ctNode = this.wrap.childNodes[1];
13105         var cs = this.elNode.childNodes;
13106         this.indentNode = cs[0];
13107         this.ecNode = cs[1];
13108         this.iconNode = cs[2];
13109         var index = 3;
13110         if(cb){
13111             this.checkbox = cs[3];
13112             index++;
13113         }
13114         this.anchor = cs[index];
13115         this.textNode = cs[index].firstChild;
13116     },
13117
13118     getAnchor : function(){
13119         return this.anchor;
13120     },
13121
13122     getTextEl : function(){
13123         return this.textNode;
13124     },
13125
13126     getIconEl : function(){
13127         return this.iconNode;
13128     },
13129
13130     isChecked : function(){
13131         return this.checkbox ? this.checkbox.checked : false;
13132     },
13133
13134     updateExpandIcon : function(){
13135         if(this.rendered){
13136             var n = this.node, c1, c2;
13137             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13138             var hasChild = n.hasChildNodes();
13139             if(hasChild){
13140                 if(n.expanded){
13141                     cls += "-minus";
13142                     c1 = "x-tree-node-collapsed";
13143                     c2 = "x-tree-node-expanded";
13144                 }else{
13145                     cls += "-plus";
13146                     c1 = "x-tree-node-expanded";
13147                     c2 = "x-tree-node-collapsed";
13148                 }
13149                 if(this.wasLeaf){
13150                     this.removeClass("x-tree-node-leaf");
13151                     this.wasLeaf = false;
13152                 }
13153                 if(this.c1 != c1 || this.c2 != c2){
13154                     Roo.fly(this.elNode).replaceClass(c1, c2);
13155                     this.c1 = c1; this.c2 = c2;
13156                 }
13157             }else{
13158                 // this changes non-leafs into leafs if they have no children.
13159                 // it's not very rational behaviour..
13160                 
13161                 if(!this.wasLeaf && this.node.leaf){
13162                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13163                     delete this.c1;
13164                     delete this.c2;
13165                     this.wasLeaf = true;
13166                 }
13167             }
13168             var ecc = "x-tree-ec-icon "+cls;
13169             if(this.ecc != ecc){
13170                 this.ecNode.className = ecc;
13171                 this.ecc = ecc;
13172             }
13173         }
13174     },
13175
13176     getChildIndent : function(){
13177         if(!this.childIndent){
13178             var buf = [];
13179             var p = this.node;
13180             while(p){
13181                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13182                     if(!p.isLast()) {
13183                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13184                     } else {
13185                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13186                     }
13187                 }
13188                 p = p.parentNode;
13189             }
13190             this.childIndent = buf.join("");
13191         }
13192         return this.childIndent;
13193     },
13194
13195     renderIndent : function(){
13196         if(this.rendered){
13197             var indent = "";
13198             var p = this.node.parentNode;
13199             if(p){
13200                 indent = p.ui.getChildIndent();
13201             }
13202             if(this.indentMarkup != indent){ // don't rerender if not required
13203                 this.indentNode.innerHTML = indent;
13204                 this.indentMarkup = indent;
13205             }
13206             this.updateExpandIcon();
13207         }
13208     }
13209 };
13210
13211 Roo.tree.RootTreeNodeUI = function(){
13212     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13213 };
13214 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13215     render : function(){
13216         if(!this.rendered){
13217             var targetNode = this.node.ownerTree.innerCt.dom;
13218             this.node.expanded = true;
13219             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13220             this.wrap = this.ctNode = targetNode.firstChild;
13221         }
13222     },
13223     collapse : function(){
13224     },
13225     expand : function(){
13226     }
13227 });/*
13228  * Based on:
13229  * Ext JS Library 1.1.1
13230  * Copyright(c) 2006-2007, Ext JS, LLC.
13231  *
13232  * Originally Released Under LGPL - original licence link has changed is not relivant.
13233  *
13234  * Fork - LGPL
13235  * <script type="text/javascript">
13236  */
13237 /**
13238  * @class Roo.tree.TreeLoader
13239  * @extends Roo.util.Observable
13240  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13241  * nodes from a specified URL. The response must be a javascript Array definition
13242  * who's elements are node definition objects. eg:
13243  * <pre><code>
13244 {  success : true,
13245    data :      [
13246    
13247     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13248     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13249     ]
13250 }
13251
13252
13253 </code></pre>
13254  * <br><br>
13255  * The old style respose with just an array is still supported, but not recommended.
13256  * <br><br>
13257  *
13258  * A server request is sent, and child nodes are loaded only when a node is expanded.
13259  * The loading node's id is passed to the server under the parameter name "node" to
13260  * enable the server to produce the correct child nodes.
13261  * <br><br>
13262  * To pass extra parameters, an event handler may be attached to the "beforeload"
13263  * event, and the parameters specified in the TreeLoader's baseParams property:
13264  * <pre><code>
13265     myTreeLoader.on("beforeload", function(treeLoader, node) {
13266         this.baseParams.category = node.attributes.category;
13267     }, this);
13268     
13269 </code></pre>
13270  *
13271  * This would pass an HTTP parameter called "category" to the server containing
13272  * the value of the Node's "category" attribute.
13273  * @constructor
13274  * Creates a new Treeloader.
13275  * @param {Object} config A config object containing config properties.
13276  */
13277 Roo.tree.TreeLoader = function(config){
13278     this.baseParams = {};
13279     this.requestMethod = "POST";
13280     Roo.apply(this, config);
13281
13282     this.addEvents({
13283     
13284         /**
13285          * @event beforeload
13286          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13287          * @param {Object} This TreeLoader object.
13288          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13289          * @param {Object} callback The callback function specified in the {@link #load} call.
13290          */
13291         beforeload : true,
13292         /**
13293          * @event load
13294          * Fires when the node has been successfuly loaded.
13295          * @param {Object} This TreeLoader object.
13296          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13297          * @param {Object} response The response object containing the data from the server.
13298          */
13299         load : true,
13300         /**
13301          * @event loadexception
13302          * Fires if the network request failed.
13303          * @param {Object} This TreeLoader object.
13304          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13305          * @param {Object} response The response object containing the data from the server.
13306          */
13307         loadexception : true,
13308         /**
13309          * @event create
13310          * Fires before a node is created, enabling you to return custom Node types 
13311          * @param {Object} This TreeLoader object.
13312          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13313          */
13314         create : true
13315     });
13316
13317     Roo.tree.TreeLoader.superclass.constructor.call(this);
13318 };
13319
13320 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13321     /**
13322     * @cfg {String} dataUrl The URL from which to request a Json string which
13323     * specifies an array of node definition object representing the child nodes
13324     * to be loaded.
13325     */
13326     /**
13327     * @cfg {String} requestMethod either GET or POST
13328     * defaults to POST (due to BC)
13329     * to be loaded.
13330     */
13331     /**
13332     * @cfg {Object} baseParams (optional) An object containing properties which
13333     * specify HTTP parameters to be passed to each request for child nodes.
13334     */
13335     /**
13336     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13337     * created by this loader. If the attributes sent by the server have an attribute in this object,
13338     * they take priority.
13339     */
13340     /**
13341     * @cfg {Object} uiProviders (optional) An object containing properties which
13342     * 
13343     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13344     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13345     * <i>uiProvider</i> attribute of a returned child node is a string rather
13346     * than a reference to a TreeNodeUI implementation, this that string value
13347     * is used as a property name in the uiProviders object. You can define the provider named
13348     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13349     */
13350     uiProviders : {},
13351
13352     /**
13353     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13354     * child nodes before loading.
13355     */
13356     clearOnLoad : true,
13357
13358     /**
13359     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13360     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13361     * Grid query { data : [ .....] }
13362     */
13363     
13364     root : false,
13365      /**
13366     * @cfg {String} queryParam (optional) 
13367     * Name of the query as it will be passed on the querystring (defaults to 'node')
13368     * eg. the request will be ?node=[id]
13369     */
13370     
13371     
13372     queryParam: false,
13373     
13374     /**
13375      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13376      * This is called automatically when a node is expanded, but may be used to reload
13377      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13378      * @param {Roo.tree.TreeNode} node
13379      * @param {Function} callback
13380      */
13381     load : function(node, callback){
13382         if(this.clearOnLoad){
13383             while(node.firstChild){
13384                 node.removeChild(node.firstChild);
13385             }
13386         }
13387         if(node.attributes.children){ // preloaded json children
13388             var cs = node.attributes.children;
13389             for(var i = 0, len = cs.length; i < len; i++){
13390                 node.appendChild(this.createNode(cs[i]));
13391             }
13392             if(typeof callback == "function"){
13393                 callback();
13394             }
13395         }else if(this.dataUrl){
13396             this.requestData(node, callback);
13397         }
13398     },
13399
13400     getParams: function(node){
13401         var buf = [], bp = this.baseParams;
13402         for(var key in bp){
13403             if(typeof bp[key] != "function"){
13404                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13405             }
13406         }
13407         var n = this.queryParam === false ? 'node' : this.queryParam;
13408         buf.push(n + "=", encodeURIComponent(node.id));
13409         return buf.join("");
13410     },
13411
13412     requestData : function(node, callback){
13413         if(this.fireEvent("beforeload", this, node, callback) !== false){
13414             this.transId = Roo.Ajax.request({
13415                 method:this.requestMethod,
13416                 url: this.dataUrl||this.url,
13417                 success: this.handleResponse,
13418                 failure: this.handleFailure,
13419                 scope: this,
13420                 argument: {callback: callback, node: node},
13421                 params: this.getParams(node)
13422             });
13423         }else{
13424             // if the load is cancelled, make sure we notify
13425             // the node that we are done
13426             if(typeof callback == "function"){
13427                 callback();
13428             }
13429         }
13430     },
13431
13432     isLoading : function(){
13433         return this.transId ? true : false;
13434     },
13435
13436     abort : function(){
13437         if(this.isLoading()){
13438             Roo.Ajax.abort(this.transId);
13439         }
13440     },
13441
13442     // private
13443     createNode : function(attr)
13444     {
13445         // apply baseAttrs, nice idea Corey!
13446         if(this.baseAttrs){
13447             Roo.applyIf(attr, this.baseAttrs);
13448         }
13449         if(this.applyLoader !== false){
13450             attr.loader = this;
13451         }
13452         // uiProvider = depreciated..
13453         
13454         if(typeof(attr.uiProvider) == 'string'){
13455            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13456                 /**  eval:var:attr */ eval(attr.uiProvider);
13457         }
13458         if(typeof(this.uiProviders['default']) != 'undefined') {
13459             attr.uiProvider = this.uiProviders['default'];
13460         }
13461         
13462         this.fireEvent('create', this, attr);
13463         
13464         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13465         return(attr.leaf ?
13466                         new Roo.tree.TreeNode(attr) :
13467                         new Roo.tree.AsyncTreeNode(attr));
13468     },
13469
13470     processResponse : function(response, node, callback)
13471     {
13472         var json = response.responseText;
13473         try {
13474             
13475             var o = Roo.decode(json);
13476             
13477             if (this.root === false && typeof(o.success) != undefined) {
13478                 this.root = 'data'; // the default behaviour for list like data..
13479                 }
13480                 
13481             if (this.root !== false &&  !o.success) {
13482                 // it's a failure condition.
13483                 var a = response.argument;
13484                 this.fireEvent("loadexception", this, a.node, response);
13485                 Roo.log("Load failed - should have a handler really");
13486                 return;
13487             }
13488             
13489             
13490             
13491             if (this.root !== false) {
13492                  o = o[this.root];
13493             }
13494             
13495             for(var i = 0, len = o.length; i < len; i++){
13496                 var n = this.createNode(o[i]);
13497                 if(n){
13498                     node.appendChild(n);
13499                 }
13500             }
13501             if(typeof callback == "function"){
13502                 callback(this, node);
13503             }
13504         }catch(e){
13505             this.handleFailure(response);
13506         }
13507     },
13508
13509     handleResponse : function(response){
13510         this.transId = false;
13511         var a = response.argument;
13512         this.processResponse(response, a.node, a.callback);
13513         this.fireEvent("load", this, a.node, response);
13514     },
13515
13516     handleFailure : function(response)
13517     {
13518         // should handle failure better..
13519         this.transId = false;
13520         var a = response.argument;
13521         this.fireEvent("loadexception", this, a.node, response);
13522         if(typeof a.callback == "function"){
13523             a.callback(this, a.node);
13524         }
13525     }
13526 });/*
13527  * Based on:
13528  * Ext JS Library 1.1.1
13529  * Copyright(c) 2006-2007, Ext JS, LLC.
13530  *
13531  * Originally Released Under LGPL - original licence link has changed is not relivant.
13532  *
13533  * Fork - LGPL
13534  * <script type="text/javascript">
13535  */
13536
13537 /**
13538 * @class Roo.tree.TreeFilter
13539 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13540 * @param {TreePanel} tree
13541 * @param {Object} config (optional)
13542  */
13543 Roo.tree.TreeFilter = function(tree, config){
13544     this.tree = tree;
13545     this.filtered = {};
13546     Roo.apply(this, config);
13547 };
13548
13549 Roo.tree.TreeFilter.prototype = {
13550     clearBlank:false,
13551     reverse:false,
13552     autoClear:false,
13553     remove:false,
13554
13555      /**
13556      * Filter the data by a specific attribute.
13557      * @param {String/RegExp} value Either string that the attribute value
13558      * should start with or a RegExp to test against the attribute
13559      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13560      * @param {TreeNode} startNode (optional) The node to start the filter at.
13561      */
13562     filter : function(value, attr, startNode){
13563         attr = attr || "text";
13564         var f;
13565         if(typeof value == "string"){
13566             var vlen = value.length;
13567             // auto clear empty filter
13568             if(vlen == 0 && this.clearBlank){
13569                 this.clear();
13570                 return;
13571             }
13572             value = value.toLowerCase();
13573             f = function(n){
13574                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13575             };
13576         }else if(value.exec){ // regex?
13577             f = function(n){
13578                 return value.test(n.attributes[attr]);
13579             };
13580         }else{
13581             throw 'Illegal filter type, must be string or regex';
13582         }
13583         this.filterBy(f, null, startNode);
13584         },
13585
13586     /**
13587      * Filter by a function. The passed function will be called with each
13588      * node in the tree (or from the startNode). If the function returns true, the node is kept
13589      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13590      * @param {Function} fn The filter function
13591      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13592      */
13593     filterBy : function(fn, scope, startNode){
13594         startNode = startNode || this.tree.root;
13595         if(this.autoClear){
13596             this.clear();
13597         }
13598         var af = this.filtered, rv = this.reverse;
13599         var f = function(n){
13600             if(n == startNode){
13601                 return true;
13602             }
13603             if(af[n.id]){
13604                 return false;
13605             }
13606             var m = fn.call(scope || n, n);
13607             if(!m || rv){
13608                 af[n.id] = n;
13609                 n.ui.hide();
13610                 return false;
13611             }
13612             return true;
13613         };
13614         startNode.cascade(f);
13615         if(this.remove){
13616            for(var id in af){
13617                if(typeof id != "function"){
13618                    var n = af[id];
13619                    if(n && n.parentNode){
13620                        n.parentNode.removeChild(n);
13621                    }
13622                }
13623            }
13624         }
13625     },
13626
13627     /**
13628      * Clears the current filter. Note: with the "remove" option
13629      * set a filter cannot be cleared.
13630      */
13631     clear : function(){
13632         var t = this.tree;
13633         var af = this.filtered;
13634         for(var id in af){
13635             if(typeof id != "function"){
13636                 var n = af[id];
13637                 if(n){
13638                     n.ui.show();
13639                 }
13640             }
13641         }
13642         this.filtered = {};
13643     }
13644 };
13645 /*
13646  * Based on:
13647  * Ext JS Library 1.1.1
13648  * Copyright(c) 2006-2007, Ext JS, LLC.
13649  *
13650  * Originally Released Under LGPL - original licence link has changed is not relivant.
13651  *
13652  * Fork - LGPL
13653  * <script type="text/javascript">
13654  */
13655  
13656
13657 /**
13658  * @class Roo.tree.TreeSorter
13659  * Provides sorting of nodes in a TreePanel
13660  * 
13661  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13662  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13663  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13664  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13665  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13666  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13667  * @constructor
13668  * @param {TreePanel} tree
13669  * @param {Object} config
13670  */
13671 Roo.tree.TreeSorter = function(tree, config){
13672     Roo.apply(this, config);
13673     tree.on("beforechildrenrendered", this.doSort, this);
13674     tree.on("append", this.updateSort, this);
13675     tree.on("insert", this.updateSort, this);
13676     
13677     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13678     var p = this.property || "text";
13679     var sortType = this.sortType;
13680     var fs = this.folderSort;
13681     var cs = this.caseSensitive === true;
13682     var leafAttr = this.leafAttr || 'leaf';
13683
13684     this.sortFn = function(n1, n2){
13685         if(fs){
13686             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13687                 return 1;
13688             }
13689             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13690                 return -1;
13691             }
13692         }
13693         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13694         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13695         if(v1 < v2){
13696                         return dsc ? +1 : -1;
13697                 }else if(v1 > v2){
13698                         return dsc ? -1 : +1;
13699         }else{
13700                 return 0;
13701         }
13702     };
13703 };
13704
13705 Roo.tree.TreeSorter.prototype = {
13706     doSort : function(node){
13707         node.sort(this.sortFn);
13708     },
13709     
13710     compareNodes : function(n1, n2){
13711         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13712     },
13713     
13714     updateSort : function(tree, node){
13715         if(node.childrenRendered){
13716             this.doSort.defer(1, this, [node]);
13717         }
13718     }
13719 };/*
13720  * Based on:
13721  * Ext JS Library 1.1.1
13722  * Copyright(c) 2006-2007, Ext JS, LLC.
13723  *
13724  * Originally Released Under LGPL - original licence link has changed is not relivant.
13725  *
13726  * Fork - LGPL
13727  * <script type="text/javascript">
13728  */
13729
13730 if(Roo.dd.DropZone){
13731     
13732 Roo.tree.TreeDropZone = function(tree, config){
13733     this.allowParentInsert = false;
13734     this.allowContainerDrop = false;
13735     this.appendOnly = false;
13736     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13737     this.tree = tree;
13738     this.lastInsertClass = "x-tree-no-status";
13739     this.dragOverData = {};
13740 };
13741
13742 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13743     ddGroup : "TreeDD",
13744     scroll:  true,
13745     
13746     expandDelay : 1000,
13747     
13748     expandNode : function(node){
13749         if(node.hasChildNodes() && !node.isExpanded()){
13750             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13751         }
13752     },
13753     
13754     queueExpand : function(node){
13755         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13756     },
13757     
13758     cancelExpand : function(){
13759         if(this.expandProcId){
13760             clearTimeout(this.expandProcId);
13761             this.expandProcId = false;
13762         }
13763     },
13764     
13765     isValidDropPoint : function(n, pt, dd, e, data){
13766         if(!n || !data){ return false; }
13767         var targetNode = n.node;
13768         var dropNode = data.node;
13769         // default drop rules
13770         if(!(targetNode && targetNode.isTarget && pt)){
13771             return false;
13772         }
13773         if(pt == "append" && targetNode.allowChildren === false){
13774             return false;
13775         }
13776         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13777             return false;
13778         }
13779         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13780             return false;
13781         }
13782         // reuse the object
13783         var overEvent = this.dragOverData;
13784         overEvent.tree = this.tree;
13785         overEvent.target = targetNode;
13786         overEvent.data = data;
13787         overEvent.point = pt;
13788         overEvent.source = dd;
13789         overEvent.rawEvent = e;
13790         overEvent.dropNode = dropNode;
13791         overEvent.cancel = false;  
13792         var result = this.tree.fireEvent("nodedragover", overEvent);
13793         return overEvent.cancel === false && result !== false;
13794     },
13795     
13796     getDropPoint : function(e, n, dd)
13797     {
13798         var tn = n.node;
13799         if(tn.isRoot){
13800             return tn.allowChildren !== false ? "append" : false; // always append for root
13801         }
13802         var dragEl = n.ddel;
13803         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13804         var y = Roo.lib.Event.getPageY(e);
13805         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13806         
13807         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13808         var noAppend = tn.allowChildren === false;
13809         if(this.appendOnly || tn.parentNode.allowChildren === false){
13810             return noAppend ? false : "append";
13811         }
13812         var noBelow = false;
13813         if(!this.allowParentInsert){
13814             noBelow = tn.hasChildNodes() && tn.isExpanded();
13815         }
13816         var q = (b - t) / (noAppend ? 2 : 3);
13817         if(y >= t && y < (t + q)){
13818             return "above";
13819         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13820             return "below";
13821         }else{
13822             return "append";
13823         }
13824     },
13825     
13826     onNodeEnter : function(n, dd, e, data)
13827     {
13828         this.cancelExpand();
13829     },
13830     
13831     onNodeOver : function(n, dd, e, data)
13832     {
13833        
13834         var pt = this.getDropPoint(e, n, dd);
13835         var node = n.node;
13836         
13837         // auto node expand check
13838         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13839             this.queueExpand(node);
13840         }else if(pt != "append"){
13841             this.cancelExpand();
13842         }
13843         
13844         // set the insert point style on the target node
13845         var returnCls = this.dropNotAllowed;
13846         if(this.isValidDropPoint(n, pt, dd, e, data)){
13847            if(pt){
13848                var el = n.ddel;
13849                var cls;
13850                if(pt == "above"){
13851                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13852                    cls = "x-tree-drag-insert-above";
13853                }else if(pt == "below"){
13854                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13855                    cls = "x-tree-drag-insert-below";
13856                }else{
13857                    returnCls = "x-tree-drop-ok-append";
13858                    cls = "x-tree-drag-append";
13859                }
13860                if(this.lastInsertClass != cls){
13861                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13862                    this.lastInsertClass = cls;
13863                }
13864            }
13865        }
13866        return returnCls;
13867     },
13868     
13869     onNodeOut : function(n, dd, e, data){
13870         
13871         this.cancelExpand();
13872         this.removeDropIndicators(n);
13873     },
13874     
13875     onNodeDrop : function(n, dd, e, data){
13876         var point = this.getDropPoint(e, n, dd);
13877         var targetNode = n.node;
13878         targetNode.ui.startDrop();
13879         if(!this.isValidDropPoint(n, point, dd, e, data)){
13880             targetNode.ui.endDrop();
13881             return false;
13882         }
13883         // first try to find the drop node
13884         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13885         var dropEvent = {
13886             tree : this.tree,
13887             target: targetNode,
13888             data: data,
13889             point: point,
13890             source: dd,
13891             rawEvent: e,
13892             dropNode: dropNode,
13893             cancel: !dropNode   
13894         };
13895         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13896         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13897             targetNode.ui.endDrop();
13898             return false;
13899         }
13900         // allow target changing
13901         targetNode = dropEvent.target;
13902         if(point == "append" && !targetNode.isExpanded()){
13903             targetNode.expand(false, null, function(){
13904                 this.completeDrop(dropEvent);
13905             }.createDelegate(this));
13906         }else{
13907             this.completeDrop(dropEvent);
13908         }
13909         return true;
13910     },
13911     
13912     completeDrop : function(de){
13913         var ns = de.dropNode, p = de.point, t = de.target;
13914         if(!(ns instanceof Array)){
13915             ns = [ns];
13916         }
13917         var n;
13918         for(var i = 0, len = ns.length; i < len; i++){
13919             n = ns[i];
13920             if(p == "above"){
13921                 t.parentNode.insertBefore(n, t);
13922             }else if(p == "below"){
13923                 t.parentNode.insertBefore(n, t.nextSibling);
13924             }else{
13925                 t.appendChild(n);
13926             }
13927         }
13928         n.ui.focus();
13929         if(this.tree.hlDrop){
13930             n.ui.highlight();
13931         }
13932         t.ui.endDrop();
13933         this.tree.fireEvent("nodedrop", de);
13934     },
13935     
13936     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13937         if(this.tree.hlDrop){
13938             dropNode.ui.focus();
13939             dropNode.ui.highlight();
13940         }
13941         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13942     },
13943     
13944     getTree : function(){
13945         return this.tree;
13946     },
13947     
13948     removeDropIndicators : function(n){
13949         if(n && n.ddel){
13950             var el = n.ddel;
13951             Roo.fly(el).removeClass([
13952                     "x-tree-drag-insert-above",
13953                     "x-tree-drag-insert-below",
13954                     "x-tree-drag-append"]);
13955             this.lastInsertClass = "_noclass";
13956         }
13957     },
13958     
13959     beforeDragDrop : function(target, e, id){
13960         this.cancelExpand();
13961         return true;
13962     },
13963     
13964     afterRepair : function(data){
13965         if(data && Roo.enableFx){
13966             data.node.ui.highlight();
13967         }
13968         this.hideProxy();
13969     } 
13970     
13971 });
13972
13973 }
13974 /*
13975  * Based on:
13976  * Ext JS Library 1.1.1
13977  * Copyright(c) 2006-2007, Ext JS, LLC.
13978  *
13979  * Originally Released Under LGPL - original licence link has changed is not relivant.
13980  *
13981  * Fork - LGPL
13982  * <script type="text/javascript">
13983  */
13984  
13985
13986 if(Roo.dd.DragZone){
13987 Roo.tree.TreeDragZone = function(tree, config){
13988     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13989     this.tree = tree;
13990 };
13991
13992 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13993     ddGroup : "TreeDD",
13994    
13995     onBeforeDrag : function(data, e){
13996         var n = data.node;
13997         return n && n.draggable && !n.disabled;
13998     },
13999      
14000     
14001     onInitDrag : function(e){
14002         var data = this.dragData;
14003         this.tree.getSelectionModel().select(data.node);
14004         this.proxy.update("");
14005         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14006         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14007     },
14008     
14009     getRepairXY : function(e, data){
14010         return data.node.ui.getDDRepairXY();
14011     },
14012     
14013     onEndDrag : function(data, e){
14014         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14015         
14016         
14017     },
14018     
14019     onValidDrop : function(dd, e, id){
14020         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14021         this.hideProxy();
14022     },
14023     
14024     beforeInvalidDrop : function(e, id){
14025         // this scrolls the original position back into view
14026         var sm = this.tree.getSelectionModel();
14027         sm.clearSelections();
14028         sm.select(this.dragData.node);
14029     }
14030 });
14031 }/*
14032  * Based on:
14033  * Ext JS Library 1.1.1
14034  * Copyright(c) 2006-2007, Ext JS, LLC.
14035  *
14036  * Originally Released Under LGPL - original licence link has changed is not relivant.
14037  *
14038  * Fork - LGPL
14039  * <script type="text/javascript">
14040  */
14041 /**
14042  * @class Roo.tree.TreeEditor
14043  * @extends Roo.Editor
14044  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14045  * as the editor field.
14046  * @constructor
14047  * @param {Object} config (used to be the tree panel.)
14048  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14049  * 
14050  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14051  * @cfg {Roo.form.TextField} field [required] The field configuration
14052  *
14053  * 
14054  */
14055 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14056     var tree = config;
14057     var field;
14058     if (oldconfig) { // old style..
14059         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14060     } else {
14061         // new style..
14062         tree = config.tree;
14063         config.field = config.field  || {};
14064         config.field.xtype = 'TextField';
14065         field = Roo.factory(config.field, Roo.form);
14066     }
14067     config = config || {};
14068     
14069     
14070     this.addEvents({
14071         /**
14072          * @event beforenodeedit
14073          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14074          * false from the handler of this event.
14075          * @param {Editor} this
14076          * @param {Roo.tree.Node} node 
14077          */
14078         "beforenodeedit" : true
14079     });
14080     
14081     //Roo.log(config);
14082     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14083
14084     this.tree = tree;
14085
14086     tree.on('beforeclick', this.beforeNodeClick, this);
14087     tree.getTreeEl().on('mousedown', this.hide, this);
14088     this.on('complete', this.updateNode, this);
14089     this.on('beforestartedit', this.fitToTree, this);
14090     this.on('startedit', this.bindScroll, this, {delay:10});
14091     this.on('specialkey', this.onSpecialKey, this);
14092 };
14093
14094 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14095     /**
14096      * @cfg {String} alignment
14097      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14098      */
14099     alignment: "l-l",
14100     // inherit
14101     autoSize: false,
14102     /**
14103      * @cfg {Boolean} hideEl
14104      * True to hide the bound element while the editor is displayed (defaults to false)
14105      */
14106     hideEl : false,
14107     /**
14108      * @cfg {String} cls
14109      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14110      */
14111     cls: "x-small-editor x-tree-editor",
14112     /**
14113      * @cfg {Boolean} shim
14114      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14115      */
14116     shim:false,
14117     // inherit
14118     shadow:"frame",
14119     /**
14120      * @cfg {Number} maxWidth
14121      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14122      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14123      * scroll and client offsets into account prior to each edit.
14124      */
14125     maxWidth: 250,
14126
14127     editDelay : 350,
14128
14129     // private
14130     fitToTree : function(ed, el){
14131         var td = this.tree.getTreeEl().dom, nd = el.dom;
14132         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14133             td.scrollLeft = nd.offsetLeft;
14134         }
14135         var w = Math.min(
14136                 this.maxWidth,
14137                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14138         this.setSize(w, '');
14139         
14140         return this.fireEvent('beforenodeedit', this, this.editNode);
14141         
14142     },
14143
14144     // private
14145     triggerEdit : function(node){
14146         this.completeEdit();
14147         this.editNode = node;
14148         this.startEdit(node.ui.textNode, node.text);
14149     },
14150
14151     // private
14152     bindScroll : function(){
14153         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14154     },
14155
14156     // private
14157     beforeNodeClick : function(node, e){
14158         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14159         this.lastClick = new Date();
14160         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14161             e.stopEvent();
14162             this.triggerEdit(node);
14163             return false;
14164         }
14165         return true;
14166     },
14167
14168     // private
14169     updateNode : function(ed, value){
14170         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14171         this.editNode.setText(value);
14172     },
14173
14174     // private
14175     onHide : function(){
14176         Roo.tree.TreeEditor.superclass.onHide.call(this);
14177         if(this.editNode){
14178             this.editNode.ui.focus();
14179         }
14180     },
14181
14182     // private
14183     onSpecialKey : function(field, e){
14184         var k = e.getKey();
14185         if(k == e.ESC){
14186             e.stopEvent();
14187             this.cancelEdit();
14188         }else if(k == e.ENTER && !e.hasModifier()){
14189             e.stopEvent();
14190             this.completeEdit();
14191         }
14192     }
14193 });//<Script type="text/javascript">
14194 /*
14195  * Based on:
14196  * Ext JS Library 1.1.1
14197  * Copyright(c) 2006-2007, Ext JS, LLC.
14198  *
14199  * Originally Released Under LGPL - original licence link has changed is not relivant.
14200  *
14201  * Fork - LGPL
14202  * <script type="text/javascript">
14203  */
14204  
14205 /**
14206  * Not documented??? - probably should be...
14207  */
14208
14209 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14210     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14211     
14212     renderElements : function(n, a, targetNode, bulkRender){
14213         //consel.log("renderElements?");
14214         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14215
14216         var t = n.getOwnerTree();
14217         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14218         
14219         var cols = t.columns;
14220         var bw = t.borderWidth;
14221         var c = cols[0];
14222         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14223          var cb = typeof a.checked == "boolean";
14224         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14225         var colcls = 'x-t-' + tid + '-c0';
14226         var buf = [
14227             '<li class="x-tree-node">',
14228             
14229                 
14230                 '<div class="x-tree-node-el ', a.cls,'">',
14231                     // extran...
14232                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14233                 
14234                 
14235                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14236                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14237                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14238                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14239                            (a.iconCls ? ' '+a.iconCls : ''),
14240                            '" unselectable="on" />',
14241                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14242                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14243                              
14244                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14245                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14246                             '<span unselectable="on" qtip="' + tx + '">',
14247                              tx,
14248                              '</span></a>' ,
14249                     '</div>',
14250                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14251                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14252                  ];
14253         for(var i = 1, len = cols.length; i < len; i++){
14254             c = cols[i];
14255             colcls = 'x-t-' + tid + '-c' +i;
14256             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14257             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14258                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14259                       "</div>");
14260          }
14261          
14262          buf.push(
14263             '</a>',
14264             '<div class="x-clear"></div></div>',
14265             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14266             "</li>");
14267         
14268         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14269             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14270                                 n.nextSibling.ui.getEl(), buf.join(""));
14271         }else{
14272             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14273         }
14274         var el = this.wrap.firstChild;
14275         this.elRow = el;
14276         this.elNode = el.firstChild;
14277         this.ranchor = el.childNodes[1];
14278         this.ctNode = this.wrap.childNodes[1];
14279         var cs = el.firstChild.childNodes;
14280         this.indentNode = cs[0];
14281         this.ecNode = cs[1];
14282         this.iconNode = cs[2];
14283         var index = 3;
14284         if(cb){
14285             this.checkbox = cs[3];
14286             index++;
14287         }
14288         this.anchor = cs[index];
14289         
14290         this.textNode = cs[index].firstChild;
14291         
14292         //el.on("click", this.onClick, this);
14293         //el.on("dblclick", this.onDblClick, this);
14294         
14295         
14296        // console.log(this);
14297     },
14298     initEvents : function(){
14299         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14300         
14301             
14302         var a = this.ranchor;
14303
14304         var el = Roo.get(a);
14305
14306         if(Roo.isOpera){ // opera render bug ignores the CSS
14307             el.setStyle("text-decoration", "none");
14308         }
14309
14310         el.on("click", this.onClick, this);
14311         el.on("dblclick", this.onDblClick, this);
14312         el.on("contextmenu", this.onContextMenu, this);
14313         
14314     },
14315     
14316     /*onSelectedChange : function(state){
14317         if(state){
14318             this.focus();
14319             this.addClass("x-tree-selected");
14320         }else{
14321             //this.blur();
14322             this.removeClass("x-tree-selected");
14323         }
14324     },*/
14325     addClass : function(cls){
14326         if(this.elRow){
14327             Roo.fly(this.elRow).addClass(cls);
14328         }
14329         
14330     },
14331     
14332     
14333     removeClass : function(cls){
14334         if(this.elRow){
14335             Roo.fly(this.elRow).removeClass(cls);
14336         }
14337     }
14338
14339     
14340     
14341 });//<Script type="text/javascript">
14342
14343 /*
14344  * Based on:
14345  * Ext JS Library 1.1.1
14346  * Copyright(c) 2006-2007, Ext JS, LLC.
14347  *
14348  * Originally Released Under LGPL - original licence link has changed is not relivant.
14349  *
14350  * Fork - LGPL
14351  * <script type="text/javascript">
14352  */
14353  
14354
14355 /**
14356  * @class Roo.tree.ColumnTree
14357  * @extends Roo.tree.TreePanel
14358  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14359  * @cfg {int} borderWidth  compined right/left border allowance
14360  * @constructor
14361  * @param {String/HTMLElement/Element} el The container element
14362  * @param {Object} config
14363  */
14364 Roo.tree.ColumnTree =  function(el, config)
14365 {
14366    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14367    this.addEvents({
14368         /**
14369         * @event resize
14370         * Fire this event on a container when it resizes
14371         * @param {int} w Width
14372         * @param {int} h Height
14373         */
14374        "resize" : true
14375     });
14376     this.on('resize', this.onResize, this);
14377 };
14378
14379 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14380     //lines:false,
14381     
14382     
14383     borderWidth: Roo.isBorderBox ? 0 : 2, 
14384     headEls : false,
14385     
14386     render : function(){
14387         // add the header.....
14388        
14389         Roo.tree.ColumnTree.superclass.render.apply(this);
14390         
14391         this.el.addClass('x-column-tree');
14392         
14393         this.headers = this.el.createChild(
14394             {cls:'x-tree-headers'},this.innerCt.dom);
14395    
14396         var cols = this.columns, c;
14397         var totalWidth = 0;
14398         this.headEls = [];
14399         var  len = cols.length;
14400         for(var i = 0; i < len; i++){
14401              c = cols[i];
14402              totalWidth += c.width;
14403             this.headEls.push(this.headers.createChild({
14404                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14405                  cn: {
14406                      cls:'x-tree-hd-text',
14407                      html: c.header
14408                  },
14409                  style:'width:'+(c.width-this.borderWidth)+'px;'
14410              }));
14411         }
14412         this.headers.createChild({cls:'x-clear'});
14413         // prevent floats from wrapping when clipped
14414         this.headers.setWidth(totalWidth);
14415         //this.innerCt.setWidth(totalWidth);
14416         this.innerCt.setStyle({ overflow: 'auto' });
14417         this.onResize(this.width, this.height);
14418              
14419         
14420     },
14421     onResize : function(w,h)
14422     {
14423         this.height = h;
14424         this.width = w;
14425         // resize cols..
14426         this.innerCt.setWidth(this.width);
14427         this.innerCt.setHeight(this.height-20);
14428         
14429         // headers...
14430         var cols = this.columns, c;
14431         var totalWidth = 0;
14432         var expEl = false;
14433         var len = cols.length;
14434         for(var i = 0; i < len; i++){
14435             c = cols[i];
14436             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14437                 // it's the expander..
14438                 expEl  = this.headEls[i];
14439                 continue;
14440             }
14441             totalWidth += c.width;
14442             
14443         }
14444         if (expEl) {
14445             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14446         }
14447         this.headers.setWidth(w-20);
14448
14449         
14450         
14451         
14452     }
14453 });
14454 /*
14455  * Based on:
14456  * Ext JS Library 1.1.1
14457  * Copyright(c) 2006-2007, Ext JS, LLC.
14458  *
14459  * Originally Released Under LGPL - original licence link has changed is not relivant.
14460  *
14461  * Fork - LGPL
14462  * <script type="text/javascript">
14463  */
14464  
14465 /**
14466  * @class Roo.menu.Menu
14467  * @extends Roo.util.Observable
14468  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14469  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14470  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14471  * @constructor
14472  * Creates a new Menu
14473  * @param {Object} config Configuration options
14474  */
14475 Roo.menu.Menu = function(config){
14476     
14477     Roo.menu.Menu.superclass.constructor.call(this, config);
14478     
14479     this.id = this.id || Roo.id();
14480     this.addEvents({
14481         /**
14482          * @event beforeshow
14483          * Fires before this menu is displayed
14484          * @param {Roo.menu.Menu} this
14485          */
14486         beforeshow : true,
14487         /**
14488          * @event beforehide
14489          * Fires before this menu is hidden
14490          * @param {Roo.menu.Menu} this
14491          */
14492         beforehide : true,
14493         /**
14494          * @event show
14495          * Fires after this menu is displayed
14496          * @param {Roo.menu.Menu} this
14497          */
14498         show : true,
14499         /**
14500          * @event hide
14501          * Fires after this menu is hidden
14502          * @param {Roo.menu.Menu} this
14503          */
14504         hide : true,
14505         /**
14506          * @event click
14507          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14508          * @param {Roo.menu.Menu} this
14509          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14510          * @param {Roo.EventObject} e
14511          */
14512         click : true,
14513         /**
14514          * @event mouseover
14515          * Fires when the mouse is hovering over this menu
14516          * @param {Roo.menu.Menu} this
14517          * @param {Roo.EventObject} e
14518          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14519          */
14520         mouseover : true,
14521         /**
14522          * @event mouseout
14523          * Fires when the mouse exits this menu
14524          * @param {Roo.menu.Menu} this
14525          * @param {Roo.EventObject} e
14526          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14527          */
14528         mouseout : true,
14529         /**
14530          * @event itemclick
14531          * Fires when a menu item contained in this menu is clicked
14532          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14533          * @param {Roo.EventObject} e
14534          */
14535         itemclick: true
14536     });
14537     if (this.registerMenu) {
14538         Roo.menu.MenuMgr.register(this);
14539     }
14540     
14541     var mis = this.items;
14542     this.items = new Roo.util.MixedCollection();
14543     if(mis){
14544         this.add.apply(this, mis);
14545     }
14546 };
14547
14548 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14549     /**
14550      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14551      */
14552     minWidth : 120,
14553     /**
14554      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14555      * for bottom-right shadow (defaults to "sides")
14556      */
14557     shadow : "sides",
14558     /**
14559      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14560      * this menu (defaults to "tl-tr?")
14561      */
14562     subMenuAlign : "tl-tr?",
14563     /**
14564      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14565      * relative to its element of origin (defaults to "tl-bl?")
14566      */
14567     defaultAlign : "tl-bl?",
14568     /**
14569      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14570      */
14571     allowOtherMenus : false,
14572     /**
14573      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14574      */
14575     registerMenu : true,
14576
14577     hidden:true,
14578
14579     // private
14580     render : function(){
14581         if(this.el){
14582             return;
14583         }
14584         var el = this.el = new Roo.Layer({
14585             cls: "x-menu",
14586             shadow:this.shadow,
14587             constrain: false,
14588             parentEl: this.parentEl || document.body,
14589             zindex:15000
14590         });
14591
14592         this.keyNav = new Roo.menu.MenuNav(this);
14593
14594         if(this.plain){
14595             el.addClass("x-menu-plain");
14596         }
14597         if(this.cls){
14598             el.addClass(this.cls);
14599         }
14600         // generic focus element
14601         this.focusEl = el.createChild({
14602             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14603         });
14604         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14605         //disabling touch- as it's causing issues ..
14606         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14607         ul.on('click'   , this.onClick, this);
14608         
14609         
14610         ul.on("mouseover", this.onMouseOver, this);
14611         ul.on("mouseout", this.onMouseOut, this);
14612         this.items.each(function(item){
14613             if (item.hidden) {
14614                 return;
14615             }
14616             
14617             var li = document.createElement("li");
14618             li.className = "x-menu-list-item";
14619             ul.dom.appendChild(li);
14620             item.render(li, this);
14621         }, this);
14622         this.ul = ul;
14623         this.autoWidth();
14624     },
14625
14626     // private
14627     autoWidth : function(){
14628         var el = this.el, ul = this.ul;
14629         if(!el){
14630             return;
14631         }
14632         var w = this.width;
14633         if(w){
14634             el.setWidth(w);
14635         }else if(Roo.isIE){
14636             el.setWidth(this.minWidth);
14637             var t = el.dom.offsetWidth; // force recalc
14638             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14639         }
14640     },
14641
14642     // private
14643     delayAutoWidth : function(){
14644         if(this.rendered){
14645             if(!this.awTask){
14646                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14647             }
14648             this.awTask.delay(20);
14649         }
14650     },
14651
14652     // private
14653     findTargetItem : function(e){
14654         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14655         if(t && t.menuItemId){
14656             return this.items.get(t.menuItemId);
14657         }
14658     },
14659
14660     // private
14661     onClick : function(e){
14662         Roo.log("menu.onClick");
14663         var t = this.findTargetItem(e);
14664         if(!t){
14665             return;
14666         }
14667         Roo.log(e);
14668         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14669             if(t == this.activeItem && t.shouldDeactivate(e)){
14670                 this.activeItem.deactivate();
14671                 delete this.activeItem;
14672                 return;
14673             }
14674             if(t.canActivate){
14675                 this.setActiveItem(t, true);
14676             }
14677             return;
14678             
14679             
14680         }
14681         
14682         t.onClick(e);
14683         this.fireEvent("click", this, t, e);
14684     },
14685
14686     // private
14687     setActiveItem : function(item, autoExpand){
14688         if(item != this.activeItem){
14689             if(this.activeItem){
14690                 this.activeItem.deactivate();
14691             }
14692             this.activeItem = item;
14693             item.activate(autoExpand);
14694         }else if(autoExpand){
14695             item.expandMenu();
14696         }
14697     },
14698
14699     // private
14700     tryActivate : function(start, step){
14701         var items = this.items;
14702         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14703             var item = items.get(i);
14704             if(!item.disabled && item.canActivate){
14705                 this.setActiveItem(item, false);
14706                 return item;
14707             }
14708         }
14709         return false;
14710     },
14711
14712     // private
14713     onMouseOver : function(e){
14714         var t;
14715         if(t = this.findTargetItem(e)){
14716             if(t.canActivate && !t.disabled){
14717                 this.setActiveItem(t, true);
14718             }
14719         }
14720         this.fireEvent("mouseover", this, e, t);
14721     },
14722
14723     // private
14724     onMouseOut : function(e){
14725         var t;
14726         if(t = this.findTargetItem(e)){
14727             if(t == this.activeItem && t.shouldDeactivate(e)){
14728                 this.activeItem.deactivate();
14729                 delete this.activeItem;
14730             }
14731         }
14732         this.fireEvent("mouseout", this, e, t);
14733     },
14734
14735     /**
14736      * Read-only.  Returns true if the menu is currently displayed, else false.
14737      * @type Boolean
14738      */
14739     isVisible : function(){
14740         return this.el && !this.hidden;
14741     },
14742
14743     /**
14744      * Displays this menu relative to another element
14745      * @param {String/HTMLElement/Roo.Element} element The element to align to
14746      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14747      * the element (defaults to this.defaultAlign)
14748      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14749      */
14750     show : function(el, pos, parentMenu){
14751         this.parentMenu = parentMenu;
14752         if(!this.el){
14753             this.render();
14754         }
14755         this.fireEvent("beforeshow", this);
14756         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14757     },
14758
14759     /**
14760      * Displays this menu at a specific xy position
14761      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14762      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14763      */
14764     showAt : function(xy, parentMenu, /* private: */_e){
14765         this.parentMenu = parentMenu;
14766         if(!this.el){
14767             this.render();
14768         }
14769         if(_e !== false){
14770             this.fireEvent("beforeshow", this);
14771             xy = this.el.adjustForConstraints(xy);
14772         }
14773         this.el.setXY(xy);
14774         this.el.show();
14775         this.hidden = false;
14776         this.focus();
14777         this.fireEvent("show", this);
14778     },
14779
14780     focus : function(){
14781         if(!this.hidden){
14782             this.doFocus.defer(50, this);
14783         }
14784     },
14785
14786     doFocus : function(){
14787         if(!this.hidden){
14788             this.focusEl.focus();
14789         }
14790     },
14791
14792     /**
14793      * Hides this menu and optionally all parent menus
14794      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14795      */
14796     hide : function(deep){
14797         if(this.el && this.isVisible()){
14798             this.fireEvent("beforehide", this);
14799             if(this.activeItem){
14800                 this.activeItem.deactivate();
14801                 this.activeItem = null;
14802             }
14803             this.el.hide();
14804             this.hidden = true;
14805             this.fireEvent("hide", this);
14806         }
14807         if(deep === true && this.parentMenu){
14808             this.parentMenu.hide(true);
14809         }
14810     },
14811
14812     /**
14813      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14814      * Any of the following are valid:
14815      * <ul>
14816      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14817      * <li>An HTMLElement object which will be converted to a menu item</li>
14818      * <li>A menu item config object that will be created as a new menu item</li>
14819      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14820      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14821      * </ul>
14822      * Usage:
14823      * <pre><code>
14824 // Create the menu
14825 var menu = new Roo.menu.Menu();
14826
14827 // Create a menu item to add by reference
14828 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14829
14830 // Add a bunch of items at once using different methods.
14831 // Only the last item added will be returned.
14832 var item = menu.add(
14833     menuItem,                // add existing item by ref
14834     'Dynamic Item',          // new TextItem
14835     '-',                     // new separator
14836     { text: 'Config Item' }  // new item by config
14837 );
14838 </code></pre>
14839      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14840      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14841      */
14842     add : function(){
14843         var a = arguments, l = a.length, item;
14844         for(var i = 0; i < l; i++){
14845             var el = a[i];
14846             if ((typeof(el) == "object") && el.xtype && el.xns) {
14847                 el = Roo.factory(el, Roo.menu);
14848             }
14849             
14850             if(el.render){ // some kind of Item
14851                 item = this.addItem(el);
14852             }else if(typeof el == "string"){ // string
14853                 if(el == "separator" || el == "-"){
14854                     item = this.addSeparator();
14855                 }else{
14856                     item = this.addText(el);
14857                 }
14858             }else if(el.tagName || el.el){ // element
14859                 item = this.addElement(el);
14860             }else if(typeof el == "object"){ // must be menu item config?
14861                 item = this.addMenuItem(el);
14862             }
14863         }
14864         return item;
14865     },
14866
14867     /**
14868      * Returns this menu's underlying {@link Roo.Element} object
14869      * @return {Roo.Element} The element
14870      */
14871     getEl : function(){
14872         if(!this.el){
14873             this.render();
14874         }
14875         return this.el;
14876     },
14877
14878     /**
14879      * Adds a separator bar to the menu
14880      * @return {Roo.menu.Item} The menu item that was added
14881      */
14882     addSeparator : function(){
14883         return this.addItem(new Roo.menu.Separator());
14884     },
14885
14886     /**
14887      * Adds an {@link Roo.Element} object to the menu
14888      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14889      * @return {Roo.menu.Item} The menu item that was added
14890      */
14891     addElement : function(el){
14892         return this.addItem(new Roo.menu.BaseItem(el));
14893     },
14894
14895     /**
14896      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14897      * @param {Roo.menu.Item} item The menu item to add
14898      * @return {Roo.menu.Item} The menu item that was added
14899      */
14900     addItem : function(item){
14901         this.items.add(item);
14902         if(this.ul){
14903             var li = document.createElement("li");
14904             li.className = "x-menu-list-item";
14905             this.ul.dom.appendChild(li);
14906             item.render(li, this);
14907             this.delayAutoWidth();
14908         }
14909         return item;
14910     },
14911
14912     /**
14913      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14914      * @param {Object} config A MenuItem config object
14915      * @return {Roo.menu.Item} The menu item that was added
14916      */
14917     addMenuItem : function(config){
14918         if(!(config instanceof Roo.menu.Item)){
14919             if(typeof config.checked == "boolean"){ // must be check menu item config?
14920                 config = new Roo.menu.CheckItem(config);
14921             }else{
14922                 config = new Roo.menu.Item(config);
14923             }
14924         }
14925         return this.addItem(config);
14926     },
14927
14928     /**
14929      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14930      * @param {String} text The text to display in the menu item
14931      * @return {Roo.menu.Item} The menu item that was added
14932      */
14933     addText : function(text){
14934         return this.addItem(new Roo.menu.TextItem({ text : text }));
14935     },
14936
14937     /**
14938      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14939      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14940      * @param {Roo.menu.Item} item The menu item to add
14941      * @return {Roo.menu.Item} The menu item that was added
14942      */
14943     insert : function(index, item){
14944         this.items.insert(index, item);
14945         if(this.ul){
14946             var li = document.createElement("li");
14947             li.className = "x-menu-list-item";
14948             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14949             item.render(li, this);
14950             this.delayAutoWidth();
14951         }
14952         return item;
14953     },
14954
14955     /**
14956      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14957      * @param {Roo.menu.Item} item The menu item to remove
14958      */
14959     remove : function(item){
14960         this.items.removeKey(item.id);
14961         item.destroy();
14962     },
14963
14964     /**
14965      * Removes and destroys all items in the menu
14966      */
14967     removeAll : function(){
14968         var f;
14969         while(f = this.items.first()){
14970             this.remove(f);
14971         }
14972     }
14973 });
14974
14975 // MenuNav is a private utility class used internally by the Menu
14976 Roo.menu.MenuNav = function(menu){
14977     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14978     this.scope = this.menu = menu;
14979 };
14980
14981 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14982     doRelay : function(e, h){
14983         var k = e.getKey();
14984         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14985             this.menu.tryActivate(0, 1);
14986             return false;
14987         }
14988         return h.call(this.scope || this, e, this.menu);
14989     },
14990
14991     up : function(e, m){
14992         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14993             m.tryActivate(m.items.length-1, -1);
14994         }
14995     },
14996
14997     down : function(e, m){
14998         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14999             m.tryActivate(0, 1);
15000         }
15001     },
15002
15003     right : function(e, m){
15004         if(m.activeItem){
15005             m.activeItem.expandMenu(true);
15006         }
15007     },
15008
15009     left : function(e, m){
15010         m.hide();
15011         if(m.parentMenu && m.parentMenu.activeItem){
15012             m.parentMenu.activeItem.activate();
15013         }
15014     },
15015
15016     enter : function(e, m){
15017         if(m.activeItem){
15018             e.stopPropagation();
15019             m.activeItem.onClick(e);
15020             m.fireEvent("click", this, m.activeItem);
15021             return true;
15022         }
15023     }
15024 });/*
15025  * Based on:
15026  * Ext JS Library 1.1.1
15027  * Copyright(c) 2006-2007, Ext JS, LLC.
15028  *
15029  * Originally Released Under LGPL - original licence link has changed is not relivant.
15030  *
15031  * Fork - LGPL
15032  * <script type="text/javascript">
15033  */
15034  
15035 /**
15036  * @class Roo.menu.MenuMgr
15037  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15038  * @static
15039  */
15040 Roo.menu.MenuMgr = function(){
15041    var menus, active, groups = {}, attached = false, lastShow = new Date();
15042
15043    // private - called when first menu is created
15044    function init(){
15045        menus = {};
15046        active = new Roo.util.MixedCollection();
15047        Roo.get(document).addKeyListener(27, function(){
15048            if(active.length > 0){
15049                hideAll();
15050            }
15051        });
15052    }
15053
15054    // private
15055    function hideAll(){
15056        if(active && active.length > 0){
15057            var c = active.clone();
15058            c.each(function(m){
15059                m.hide();
15060            });
15061        }
15062    }
15063
15064    // private
15065    function onHide(m){
15066        active.remove(m);
15067        if(active.length < 1){
15068            Roo.get(document).un("mousedown", onMouseDown);
15069            attached = false;
15070        }
15071    }
15072
15073    // private
15074    function onShow(m){
15075        var last = active.last();
15076        lastShow = new Date();
15077        active.add(m);
15078        if(!attached){
15079            Roo.get(document).on("mousedown", onMouseDown);
15080            attached = true;
15081        }
15082        if(m.parentMenu){
15083           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15084           m.parentMenu.activeChild = m;
15085        }else if(last && last.isVisible()){
15086           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15087        }
15088    }
15089
15090    // private
15091    function onBeforeHide(m){
15092        if(m.activeChild){
15093            m.activeChild.hide();
15094        }
15095        if(m.autoHideTimer){
15096            clearTimeout(m.autoHideTimer);
15097            delete m.autoHideTimer;
15098        }
15099    }
15100
15101    // private
15102    function onBeforeShow(m){
15103        var pm = m.parentMenu;
15104        if(!pm && !m.allowOtherMenus){
15105            hideAll();
15106        }else if(pm && pm.activeChild && active != m){
15107            pm.activeChild.hide();
15108        }
15109    }
15110
15111    // private
15112    function onMouseDown(e){
15113        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15114            hideAll();
15115        }
15116    }
15117
15118    // private
15119    function onBeforeCheck(mi, state){
15120        if(state){
15121            var g = groups[mi.group];
15122            for(var i = 0, l = g.length; i < l; i++){
15123                if(g[i] != mi){
15124                    g[i].setChecked(false);
15125                }
15126            }
15127        }
15128    }
15129
15130    return {
15131
15132        /**
15133         * Hides all menus that are currently visible
15134         */
15135        hideAll : function(){
15136             hideAll();  
15137        },
15138
15139        // private
15140        register : function(menu){
15141            if(!menus){
15142                init();
15143            }
15144            menus[menu.id] = menu;
15145            menu.on("beforehide", onBeforeHide);
15146            menu.on("hide", onHide);
15147            menu.on("beforeshow", onBeforeShow);
15148            menu.on("show", onShow);
15149            var g = menu.group;
15150            if(g && menu.events["checkchange"]){
15151                if(!groups[g]){
15152                    groups[g] = [];
15153                }
15154                groups[g].push(menu);
15155                menu.on("checkchange", onCheck);
15156            }
15157        },
15158
15159         /**
15160          * Returns a {@link Roo.menu.Menu} object
15161          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15162          * be used to generate and return a new Menu instance.
15163          */
15164        get : function(menu){
15165            if(typeof menu == "string"){ // menu id
15166                return menus[menu];
15167            }else if(menu.events){  // menu instance
15168                return menu;
15169            }else if(typeof menu.length == 'number'){ // array of menu items?
15170                return new Roo.menu.Menu({items:menu});
15171            }else{ // otherwise, must be a config
15172                return new Roo.menu.Menu(menu);
15173            }
15174        },
15175
15176        // private
15177        unregister : function(menu){
15178            delete menus[menu.id];
15179            menu.un("beforehide", onBeforeHide);
15180            menu.un("hide", onHide);
15181            menu.un("beforeshow", onBeforeShow);
15182            menu.un("show", onShow);
15183            var g = menu.group;
15184            if(g && menu.events["checkchange"]){
15185                groups[g].remove(menu);
15186                menu.un("checkchange", onCheck);
15187            }
15188        },
15189
15190        // private
15191        registerCheckable : function(menuItem){
15192            var g = menuItem.group;
15193            if(g){
15194                if(!groups[g]){
15195                    groups[g] = [];
15196                }
15197                groups[g].push(menuItem);
15198                menuItem.on("beforecheckchange", onBeforeCheck);
15199            }
15200        },
15201
15202        // private
15203        unregisterCheckable : function(menuItem){
15204            var g = menuItem.group;
15205            if(g){
15206                groups[g].remove(menuItem);
15207                menuItem.un("beforecheckchange", onBeforeCheck);
15208            }
15209        }
15210    };
15211 }();/*
15212  * Based on:
15213  * Ext JS Library 1.1.1
15214  * Copyright(c) 2006-2007, Ext JS, LLC.
15215  *
15216  * Originally Released Under LGPL - original licence link has changed is not relivant.
15217  *
15218  * Fork - LGPL
15219  * <script type="text/javascript">
15220  */
15221  
15222
15223 /**
15224  * @class Roo.menu.BaseItem
15225  * @extends Roo.Component
15226  * @abstract
15227  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15228  * management and base configuration options shared by all menu components.
15229  * @constructor
15230  * Creates a new BaseItem
15231  * @param {Object} config Configuration options
15232  */
15233 Roo.menu.BaseItem = function(config){
15234     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15235
15236     this.addEvents({
15237         /**
15238          * @event click
15239          * Fires when this item is clicked
15240          * @param {Roo.menu.BaseItem} this
15241          * @param {Roo.EventObject} e
15242          */
15243         click: true,
15244         /**
15245          * @event activate
15246          * Fires when this item is activated
15247          * @param {Roo.menu.BaseItem} this
15248          */
15249         activate : true,
15250         /**
15251          * @event deactivate
15252          * Fires when this item is deactivated
15253          * @param {Roo.menu.BaseItem} this
15254          */
15255         deactivate : true
15256     });
15257
15258     if(this.handler){
15259         this.on("click", this.handler, this.scope, true);
15260     }
15261 };
15262
15263 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15264     /**
15265      * @cfg {Function} handler
15266      * A function that will handle the click event of this menu item (defaults to undefined)
15267      */
15268     /**
15269      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15270      */
15271     canActivate : false,
15272     
15273      /**
15274      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15275      */
15276     hidden: false,
15277     
15278     /**
15279      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15280      */
15281     activeClass : "x-menu-item-active",
15282     /**
15283      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15284      */
15285     hideOnClick : true,
15286     /**
15287      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15288      */
15289     hideDelay : 100,
15290
15291     // private
15292     ctype: "Roo.menu.BaseItem",
15293
15294     // private
15295     actionMode : "container",
15296
15297     // private
15298     render : function(container, parentMenu){
15299         this.parentMenu = parentMenu;
15300         Roo.menu.BaseItem.superclass.render.call(this, container);
15301         this.container.menuItemId = this.id;
15302     },
15303
15304     // private
15305     onRender : function(container, position){
15306         this.el = Roo.get(this.el);
15307         container.dom.appendChild(this.el.dom);
15308     },
15309
15310     // private
15311     onClick : function(e){
15312         if(!this.disabled && this.fireEvent("click", this, e) !== false
15313                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15314             this.handleClick(e);
15315         }else{
15316             e.stopEvent();
15317         }
15318     },
15319
15320     // private
15321     activate : function(){
15322         if(this.disabled){
15323             return false;
15324         }
15325         var li = this.container;
15326         li.addClass(this.activeClass);
15327         this.region = li.getRegion().adjust(2, 2, -2, -2);
15328         this.fireEvent("activate", this);
15329         return true;
15330     },
15331
15332     // private
15333     deactivate : function(){
15334         this.container.removeClass(this.activeClass);
15335         this.fireEvent("deactivate", this);
15336     },
15337
15338     // private
15339     shouldDeactivate : function(e){
15340         return !this.region || !this.region.contains(e.getPoint());
15341     },
15342
15343     // private
15344     handleClick : function(e){
15345         if(this.hideOnClick){
15346             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15347         }
15348     },
15349
15350     // private
15351     expandMenu : function(autoActivate){
15352         // do nothing
15353     },
15354
15355     // private
15356     hideMenu : function(){
15357         // do nothing
15358     }
15359 });/*
15360  * Based on:
15361  * Ext JS Library 1.1.1
15362  * Copyright(c) 2006-2007, Ext JS, LLC.
15363  *
15364  * Originally Released Under LGPL - original licence link has changed is not relivant.
15365  *
15366  * Fork - LGPL
15367  * <script type="text/javascript">
15368  */
15369  
15370 /**
15371  * @class Roo.menu.Adapter
15372  * @extends Roo.menu.BaseItem
15373  * @abstract
15374  * 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.
15375  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15376  * @constructor
15377  * Creates a new Adapter
15378  * @param {Object} config Configuration options
15379  */
15380 Roo.menu.Adapter = function(component, config){
15381     Roo.menu.Adapter.superclass.constructor.call(this, config);
15382     this.component = component;
15383 };
15384 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15385     // private
15386     canActivate : true,
15387
15388     // private
15389     onRender : function(container, position){
15390         this.component.render(container);
15391         this.el = this.component.getEl();
15392     },
15393
15394     // private
15395     activate : function(){
15396         if(this.disabled){
15397             return false;
15398         }
15399         this.component.focus();
15400         this.fireEvent("activate", this);
15401         return true;
15402     },
15403
15404     // private
15405     deactivate : function(){
15406         this.fireEvent("deactivate", this);
15407     },
15408
15409     // private
15410     disable : function(){
15411         this.component.disable();
15412         Roo.menu.Adapter.superclass.disable.call(this);
15413     },
15414
15415     // private
15416     enable : function(){
15417         this.component.enable();
15418         Roo.menu.Adapter.superclass.enable.call(this);
15419     }
15420 });/*
15421  * Based on:
15422  * Ext JS Library 1.1.1
15423  * Copyright(c) 2006-2007, Ext JS, LLC.
15424  *
15425  * Originally Released Under LGPL - original licence link has changed is not relivant.
15426  *
15427  * Fork - LGPL
15428  * <script type="text/javascript">
15429  */
15430
15431 /**
15432  * @class Roo.menu.TextItem
15433  * @extends Roo.menu.BaseItem
15434  * Adds a static text string to a menu, usually used as either a heading or group separator.
15435  * Note: old style constructor with text is still supported.
15436  * 
15437  * @constructor
15438  * Creates a new TextItem
15439  * @param {Object} cfg Configuration
15440  */
15441 Roo.menu.TextItem = function(cfg){
15442     if (typeof(cfg) == 'string') {
15443         this.text = cfg;
15444     } else {
15445         Roo.apply(this,cfg);
15446     }
15447     
15448     Roo.menu.TextItem.superclass.constructor.call(this);
15449 };
15450
15451 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15452     /**
15453      * @cfg {String} text Text to show on item.
15454      */
15455     text : '',
15456     
15457     /**
15458      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15459      */
15460     hideOnClick : false,
15461     /**
15462      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15463      */
15464     itemCls : "x-menu-text",
15465
15466     // private
15467     onRender : function(){
15468         var s = document.createElement("span");
15469         s.className = this.itemCls;
15470         s.innerHTML = this.text;
15471         this.el = s;
15472         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15473     }
15474 });/*
15475  * Based on:
15476  * Ext JS Library 1.1.1
15477  * Copyright(c) 2006-2007, Ext JS, LLC.
15478  *
15479  * Originally Released Under LGPL - original licence link has changed is not relivant.
15480  *
15481  * Fork - LGPL
15482  * <script type="text/javascript">
15483  */
15484
15485 /**
15486  * @class Roo.menu.Separator
15487  * @extends Roo.menu.BaseItem
15488  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15489  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15490  * @constructor
15491  * @param {Object} config Configuration options
15492  */
15493 Roo.menu.Separator = function(config){
15494     Roo.menu.Separator.superclass.constructor.call(this, config);
15495 };
15496
15497 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15498     /**
15499      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15500      */
15501     itemCls : "x-menu-sep",
15502     /**
15503      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15504      */
15505     hideOnClick : false,
15506
15507     // private
15508     onRender : function(li){
15509         var s = document.createElement("span");
15510         s.className = this.itemCls;
15511         s.innerHTML = "&#160;";
15512         this.el = s;
15513         li.addClass("x-menu-sep-li");
15514         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15515     }
15516 });/*
15517  * Based on:
15518  * Ext JS Library 1.1.1
15519  * Copyright(c) 2006-2007, Ext JS, LLC.
15520  *
15521  * Originally Released Under LGPL - original licence link has changed is not relivant.
15522  *
15523  * Fork - LGPL
15524  * <script type="text/javascript">
15525  */
15526 /**
15527  * @class Roo.menu.Item
15528  * @extends Roo.menu.BaseItem
15529  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15530  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15531  * activation and click handling.
15532  * @constructor
15533  * Creates a new Item
15534  * @param {Object} config Configuration options
15535  */
15536 Roo.menu.Item = function(config){
15537     Roo.menu.Item.superclass.constructor.call(this, config);
15538     if(this.menu){
15539         this.menu = Roo.menu.MenuMgr.get(this.menu);
15540     }
15541 };
15542 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15543     /**
15544      * @cfg {Roo.menu.Menu} menu
15545      * A Sub menu
15546      */
15547     /**
15548      * @cfg {String} text
15549      * The text to show on the menu item.
15550      */
15551     text: '',
15552      /**
15553      * @cfg {String} html to render in menu
15554      * The text to show on the menu item (HTML version).
15555      */
15556     html: '',
15557     /**
15558      * @cfg {String} icon
15559      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15560      */
15561     icon: undefined,
15562     /**
15563      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15564      */
15565     itemCls : "x-menu-item",
15566     /**
15567      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15568      */
15569     canActivate : true,
15570     /**
15571      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15572      */
15573     showDelay: 200,
15574     // doc'd in BaseItem
15575     hideDelay: 200,
15576
15577     // private
15578     ctype: "Roo.menu.Item",
15579     
15580     // private
15581     onRender : function(container, position){
15582         var el = document.createElement("a");
15583         el.hideFocus = true;
15584         el.unselectable = "on";
15585         el.href = this.href || "#";
15586         if(this.hrefTarget){
15587             el.target = this.hrefTarget;
15588         }
15589         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15590         
15591         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15592         
15593         el.innerHTML = String.format(
15594                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15595                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15596         this.el = el;
15597         Roo.menu.Item.superclass.onRender.call(this, container, position);
15598     },
15599
15600     /**
15601      * Sets the text to display in this menu item
15602      * @param {String} text The text to display
15603      * @param {Boolean} isHTML true to indicate text is pure html.
15604      */
15605     setText : function(text, isHTML){
15606         if (isHTML) {
15607             this.html = text;
15608         } else {
15609             this.text = text;
15610             this.html = '';
15611         }
15612         if(this.rendered){
15613             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15614      
15615             this.el.update(String.format(
15616                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15617                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15618             this.parentMenu.autoWidth();
15619         }
15620     },
15621
15622     // private
15623     handleClick : function(e){
15624         if(!this.href){ // if no link defined, stop the event automatically
15625             e.stopEvent();
15626         }
15627         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15628     },
15629
15630     // private
15631     activate : function(autoExpand){
15632         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15633             this.focus();
15634             if(autoExpand){
15635                 this.expandMenu();
15636             }
15637         }
15638         return true;
15639     },
15640
15641     // private
15642     shouldDeactivate : function(e){
15643         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15644             if(this.menu && this.menu.isVisible()){
15645                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15646             }
15647             return true;
15648         }
15649         return false;
15650     },
15651
15652     // private
15653     deactivate : function(){
15654         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15655         this.hideMenu();
15656     },
15657
15658     // private
15659     expandMenu : function(autoActivate){
15660         if(!this.disabled && this.menu){
15661             clearTimeout(this.hideTimer);
15662             delete this.hideTimer;
15663             if(!this.menu.isVisible() && !this.showTimer){
15664                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15665             }else if (this.menu.isVisible() && autoActivate){
15666                 this.menu.tryActivate(0, 1);
15667             }
15668         }
15669     },
15670
15671     // private
15672     deferExpand : function(autoActivate){
15673         delete this.showTimer;
15674         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15675         if(autoActivate){
15676             this.menu.tryActivate(0, 1);
15677         }
15678     },
15679
15680     // private
15681     hideMenu : function(){
15682         clearTimeout(this.showTimer);
15683         delete this.showTimer;
15684         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15685             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15686         }
15687     },
15688
15689     // private
15690     deferHide : function(){
15691         delete this.hideTimer;
15692         this.menu.hide();
15693     }
15694 });/*
15695  * Based on:
15696  * Ext JS Library 1.1.1
15697  * Copyright(c) 2006-2007, Ext JS, LLC.
15698  *
15699  * Originally Released Under LGPL - original licence link has changed is not relivant.
15700  *
15701  * Fork - LGPL
15702  * <script type="text/javascript">
15703  */
15704  
15705 /**
15706  * @class Roo.menu.CheckItem
15707  * @extends Roo.menu.Item
15708  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15709  * @constructor
15710  * Creates a new CheckItem
15711  * @param {Object} config Configuration options
15712  */
15713 Roo.menu.CheckItem = function(config){
15714     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15715     this.addEvents({
15716         /**
15717          * @event beforecheckchange
15718          * Fires before the checked value is set, providing an opportunity to cancel if needed
15719          * @param {Roo.menu.CheckItem} this
15720          * @param {Boolean} checked The new checked value that will be set
15721          */
15722         "beforecheckchange" : true,
15723         /**
15724          * @event checkchange
15725          * Fires after the checked value has been set
15726          * @param {Roo.menu.CheckItem} this
15727          * @param {Boolean} checked The checked value that was set
15728          */
15729         "checkchange" : true
15730     });
15731     if(this.checkHandler){
15732         this.on('checkchange', this.checkHandler, this.scope);
15733     }
15734 };
15735 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15736     /**
15737      * @cfg {String} group
15738      * All check items with the same group name will automatically be grouped into a single-select
15739      * radio button group (defaults to '')
15740      */
15741     /**
15742      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15743      */
15744     itemCls : "x-menu-item x-menu-check-item",
15745     /**
15746      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15747      */
15748     groupClass : "x-menu-group-item",
15749
15750     /**
15751      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15752      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15753      * initialized with checked = true will be rendered as checked.
15754      */
15755     checked: false,
15756
15757     // private
15758     ctype: "Roo.menu.CheckItem",
15759
15760     // private
15761     onRender : function(c){
15762         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15763         if(this.group){
15764             this.el.addClass(this.groupClass);
15765         }
15766         Roo.menu.MenuMgr.registerCheckable(this);
15767         if(this.checked){
15768             this.checked = false;
15769             this.setChecked(true, true);
15770         }
15771     },
15772
15773     // private
15774     destroy : function(){
15775         if(this.rendered){
15776             Roo.menu.MenuMgr.unregisterCheckable(this);
15777         }
15778         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15779     },
15780
15781     /**
15782      * Set the checked state of this item
15783      * @param {Boolean} checked The new checked value
15784      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15785      */
15786     setChecked : function(state, suppressEvent){
15787         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15788             if(this.container){
15789                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15790             }
15791             this.checked = state;
15792             if(suppressEvent !== true){
15793                 this.fireEvent("checkchange", this, state);
15794             }
15795         }
15796     },
15797
15798     // private
15799     handleClick : function(e){
15800        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15801            this.setChecked(!this.checked);
15802        }
15803        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15804     }
15805 });/*
15806  * Based on:
15807  * Ext JS Library 1.1.1
15808  * Copyright(c) 2006-2007, Ext JS, LLC.
15809  *
15810  * Originally Released Under LGPL - original licence link has changed is not relivant.
15811  *
15812  * Fork - LGPL
15813  * <script type="text/javascript">
15814  */
15815  
15816 /**
15817  * @class Roo.menu.DateItem
15818  * @extends Roo.menu.Adapter
15819  * A menu item that wraps the {@link Roo.DatPicker} component.
15820  * @constructor
15821  * Creates a new DateItem
15822  * @param {Object} config Configuration options
15823  */
15824 Roo.menu.DateItem = function(config){
15825     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15826     /** The Roo.DatePicker object @type Roo.DatePicker */
15827     this.picker = this.component;
15828     this.addEvents({select: true});
15829     
15830     this.picker.on("render", function(picker){
15831         picker.getEl().swallowEvent("click");
15832         picker.container.addClass("x-menu-date-item");
15833     });
15834
15835     this.picker.on("select", this.onSelect, this);
15836 };
15837
15838 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15839     // private
15840     onSelect : function(picker, date){
15841         this.fireEvent("select", this, date, picker);
15842         Roo.menu.DateItem.superclass.handleClick.call(this);
15843     }
15844 });/*
15845  * Based on:
15846  * Ext JS Library 1.1.1
15847  * Copyright(c) 2006-2007, Ext JS, LLC.
15848  *
15849  * Originally Released Under LGPL - original licence link has changed is not relivant.
15850  *
15851  * Fork - LGPL
15852  * <script type="text/javascript">
15853  */
15854  
15855 /**
15856  * @class Roo.menu.ColorItem
15857  * @extends Roo.menu.Adapter
15858  * A menu item that wraps the {@link Roo.ColorPalette} component.
15859  * @constructor
15860  * Creates a new ColorItem
15861  * @param {Object} config Configuration options
15862  */
15863 Roo.menu.ColorItem = function(config){
15864     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15865     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15866     this.palette = this.component;
15867     this.relayEvents(this.palette, ["select"]);
15868     if(this.selectHandler){
15869         this.on('select', this.selectHandler, this.scope);
15870     }
15871 };
15872 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15873  * Based on:
15874  * Ext JS Library 1.1.1
15875  * Copyright(c) 2006-2007, Ext JS, LLC.
15876  *
15877  * Originally Released Under LGPL - original licence link has changed is not relivant.
15878  *
15879  * Fork - LGPL
15880  * <script type="text/javascript">
15881  */
15882  
15883
15884 /**
15885  * @class Roo.menu.DateMenu
15886  * @extends Roo.menu.Menu
15887  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15888  * @constructor
15889  * Creates a new DateMenu
15890  * @param {Object} config Configuration options
15891  */
15892 Roo.menu.DateMenu = function(config){
15893     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15894     this.plain = true;
15895     var di = new Roo.menu.DateItem(config);
15896     this.add(di);
15897     /**
15898      * The {@link Roo.DatePicker} instance for this DateMenu
15899      * @type DatePicker
15900      */
15901     this.picker = di.picker;
15902     /**
15903      * @event select
15904      * @param {DatePicker} picker
15905      * @param {Date} date
15906      */
15907     this.relayEvents(di, ["select"]);
15908     this.on('beforeshow', function(){
15909         if(this.picker){
15910             this.picker.hideMonthPicker(false);
15911         }
15912     }, this);
15913 };
15914 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15915     cls:'x-date-menu'
15916 });/*
15917  * Based on:
15918  * Ext JS Library 1.1.1
15919  * Copyright(c) 2006-2007, Ext JS, LLC.
15920  *
15921  * Originally Released Under LGPL - original licence link has changed is not relivant.
15922  *
15923  * Fork - LGPL
15924  * <script type="text/javascript">
15925  */
15926  
15927
15928 /**
15929  * @class Roo.menu.ColorMenu
15930  * @extends Roo.menu.Menu
15931  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15932  * @constructor
15933  * Creates a new ColorMenu
15934  * @param {Object} config Configuration options
15935  */
15936 Roo.menu.ColorMenu = function(config){
15937     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15938     this.plain = true;
15939     var ci = new Roo.menu.ColorItem(config);
15940     this.add(ci);
15941     /**
15942      * The {@link Roo.ColorPalette} instance for this ColorMenu
15943      * @type ColorPalette
15944      */
15945     this.palette = ci.palette;
15946     /**
15947      * @event select
15948      * @param {ColorPalette} palette
15949      * @param {String} color
15950      */
15951     this.relayEvents(ci, ["select"]);
15952 };
15953 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15954  * Based on:
15955  * Ext JS Library 1.1.1
15956  * Copyright(c) 2006-2007, Ext JS, LLC.
15957  *
15958  * Originally Released Under LGPL - original licence link has changed is not relivant.
15959  *
15960  * Fork - LGPL
15961  * <script type="text/javascript">
15962  */
15963  
15964 /**
15965  * @class Roo.form.TextItem
15966  * @extends Roo.BoxComponent
15967  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15968  * @constructor
15969  * Creates a new TextItem
15970  * @param {Object} config Configuration options
15971  */
15972 Roo.form.TextItem = function(config){
15973     Roo.form.TextItem.superclass.constructor.call(this, config);
15974 };
15975
15976 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15977     
15978     /**
15979      * @cfg {String} tag the tag for this item (default div)
15980      */
15981     tag : 'div',
15982     /**
15983      * @cfg {String} html the content for this item
15984      */
15985     html : '',
15986     
15987     getAutoCreate : function()
15988     {
15989         var cfg = {
15990             id: this.id,
15991             tag: this.tag,
15992             html: this.html,
15993             cls: 'x-form-item'
15994         };
15995         
15996         return cfg;
15997         
15998     },
15999     
16000     onRender : function(ct, position)
16001     {
16002         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16003         
16004         if(!this.el){
16005             var cfg = this.getAutoCreate();
16006             if(!cfg.name){
16007                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16008             }
16009             if (!cfg.name.length) {
16010                 delete cfg.name;
16011             }
16012             this.el = ct.createChild(cfg, position);
16013         }
16014     },
16015     /*
16016      * setHTML
16017      * @param {String} html update the Contents of the element.
16018      */
16019     setHTML : function(html)
16020     {
16021         this.fieldEl.dom.innerHTML = html;
16022     }
16023     
16024 });/*
16025  * Based on:
16026  * Ext JS Library 1.1.1
16027  * Copyright(c) 2006-2007, Ext JS, LLC.
16028  *
16029  * Originally Released Under LGPL - original licence link has changed is not relivant.
16030  *
16031  * Fork - LGPL
16032  * <script type="text/javascript">
16033  */
16034  
16035 /**
16036  * @class Roo.form.Field
16037  * @extends Roo.BoxComponent
16038  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16039  * @constructor
16040  * Creates a new Field
16041  * @param {Object} config Configuration options
16042  */
16043 Roo.form.Field = function(config){
16044     Roo.form.Field.superclass.constructor.call(this, config);
16045 };
16046
16047 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16048     /**
16049      * @cfg {String} fieldLabel Label to use when rendering a form.
16050      */
16051         /**
16052      * @cfg {String} labelSeparator the ':' after a field label (default :)  = set it to empty string to hide the field label.
16053      */
16054        /**
16055      * @cfg {String} qtip Mouse over tip
16056      */
16057      
16058     /**
16059      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16060      */
16061     invalidClass : "x-form-invalid",
16062     /**
16063      * @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")
16064      */
16065     invalidText : "The value in this field is invalid",
16066     /**
16067      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16068      */
16069     focusClass : "x-form-focus",
16070     /**
16071      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16072       automatic validation (defaults to "keyup").
16073      */
16074     validationEvent : "keyup",
16075     /**
16076      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16077      */
16078     validateOnBlur : true,
16079     /**
16080      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16081      */
16082     validationDelay : 250,
16083     /**
16084      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16085      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16086      */
16087     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16088     /**
16089      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16090      */
16091     fieldClass : "x-form-field",
16092     /**
16093      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16094      *<pre>
16095 Value         Description
16096 -----------   ----------------------------------------------------------------------
16097 qtip          Display a quick tip when the user hovers over the field
16098 title         Display a default browser title attribute popup
16099 under         Add a block div beneath the field containing the error text
16100 side          Add an error icon to the right of the field with a popup on hover
16101 [element id]  Add the error text directly to the innerHTML of the specified element
16102 </pre>
16103      */
16104     msgTarget : 'qtip',
16105     /**
16106      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16107      */
16108     msgFx : 'normal',
16109
16110     /**
16111      * @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.
16112      */
16113     readOnly : false,
16114
16115     /**
16116      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16117      */
16118     disabled : false,
16119
16120     /**
16121      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16122      */
16123     inputType : undefined,
16124     
16125     /**
16126      * @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).
16127          */
16128         tabIndex : undefined,
16129         
16130     // private
16131     isFormField : true,
16132
16133     // private
16134     hasFocus : false,
16135     /**
16136      * @property {Roo.Element} fieldEl
16137      * Element Containing the rendered Field (with label etc.)
16138      */
16139     /**
16140      * @cfg {Mixed} value A value to initialize this field with.
16141      */
16142     value : undefined,
16143
16144     /**
16145      * @cfg {String} name The field's HTML name attribute.
16146      */
16147     /**
16148      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16149      */
16150     // private
16151     loadedValue : false,
16152      
16153      
16154         // private ??
16155         initComponent : function(){
16156         Roo.form.Field.superclass.initComponent.call(this);
16157         this.addEvents({
16158             /**
16159              * @event focus
16160              * Fires when this field receives input focus.
16161              * @param {Roo.form.Field} this
16162              */
16163             focus : true,
16164             /**
16165              * @event blur
16166              * Fires when this field loses input focus.
16167              * @param {Roo.form.Field} this
16168              */
16169             blur : true,
16170             /**
16171              * @event specialkey
16172              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16173              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16174              * @param {Roo.form.Field} this
16175              * @param {Roo.EventObject} e The event object
16176              */
16177             specialkey : true,
16178             /**
16179              * @event change
16180              * Fires just before the field blurs if the field value has changed.
16181              * @param {Roo.form.Field} this
16182              * @param {Mixed} newValue The new value
16183              * @param {Mixed} oldValue The original value
16184              */
16185             change : true,
16186             /**
16187              * @event invalid
16188              * Fires after the field has been marked as invalid.
16189              * @param {Roo.form.Field} this
16190              * @param {String} msg The validation message
16191              */
16192             invalid : true,
16193             /**
16194              * @event valid
16195              * Fires after the field has been validated with no errors.
16196              * @param {Roo.form.Field} this
16197              */
16198             valid : true,
16199              /**
16200              * @event keyup
16201              * Fires after the key up
16202              * @param {Roo.form.Field} this
16203              * @param {Roo.EventObject}  e The event Object
16204              */
16205             keyup : true
16206         });
16207     },
16208
16209     /**
16210      * Returns the name attribute of the field if available
16211      * @return {String} name The field name
16212      */
16213     getName: function(){
16214          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16215     },
16216
16217     // private
16218     onRender : function(ct, position){
16219         Roo.form.Field.superclass.onRender.call(this, ct, position);
16220         if(!this.el){
16221             var cfg = this.getAutoCreate();
16222             if(!cfg.name){
16223                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16224             }
16225             if (!cfg.name.length) {
16226                 delete cfg.name;
16227             }
16228             if(this.inputType){
16229                 cfg.type = this.inputType;
16230             }
16231             this.el = ct.createChild(cfg, position);
16232         }
16233         var type = this.el.dom.type;
16234         if(type){
16235             if(type == 'password'){
16236                 type = 'text';
16237             }
16238             this.el.addClass('x-form-'+type);
16239         }
16240         if(this.readOnly){
16241             this.el.dom.readOnly = true;
16242         }
16243         if(this.tabIndex !== undefined){
16244             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16245         }
16246
16247         this.el.addClass([this.fieldClass, this.cls]);
16248         this.initValue();
16249     },
16250
16251     /**
16252      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16253      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16254      * @return {Roo.form.Field} this
16255      */
16256     applyTo : function(target){
16257         this.allowDomMove = false;
16258         this.el = Roo.get(target);
16259         this.render(this.el.dom.parentNode);
16260         return this;
16261     },
16262
16263     // private
16264     initValue : function(){
16265         if(this.value !== undefined){
16266             this.setValue(this.value);
16267         }else if(this.el.dom.value.length > 0){
16268             this.setValue(this.el.dom.value);
16269         }
16270     },
16271
16272     /**
16273      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16274      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16275      */
16276     isDirty : function() {
16277         if(this.disabled) {
16278             return false;
16279         }
16280         return String(this.getValue()) !== String(this.originalValue);
16281     },
16282
16283     /**
16284      * stores the current value in loadedValue
16285      */
16286     resetHasChanged : function()
16287     {
16288         this.loadedValue = String(this.getValue());
16289     },
16290     /**
16291      * checks the current value against the 'loaded' value.
16292      * Note - will return false if 'resetHasChanged' has not been called first.
16293      */
16294     hasChanged : function()
16295     {
16296         if(this.disabled || this.readOnly) {
16297             return false;
16298         }
16299         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16300     },
16301     
16302     
16303     
16304     // private
16305     afterRender : function(){
16306         Roo.form.Field.superclass.afterRender.call(this);
16307         this.initEvents();
16308     },
16309
16310     // private
16311     fireKey : function(e){
16312         //Roo.log('field ' + e.getKey());
16313         if(e.isNavKeyPress()){
16314             this.fireEvent("specialkey", this, e);
16315         }
16316     },
16317
16318     /**
16319      * Resets the current field value to the originally loaded value and clears any validation messages
16320      */
16321     reset : function(){
16322         this.setValue(this.resetValue);
16323         this.originalValue = this.getValue();
16324         this.clearInvalid();
16325     },
16326
16327     // private
16328     initEvents : function(){
16329         // safari killled keypress - so keydown is now used..
16330         this.el.on("keydown" , this.fireKey,  this);
16331         this.el.on("focus", this.onFocus,  this);
16332         this.el.on("blur", this.onBlur,  this);
16333         this.el.relayEvent('keyup', this);
16334
16335         // reference to original value for reset
16336         this.originalValue = this.getValue();
16337         this.resetValue =  this.getValue();
16338     },
16339
16340     // private
16341     onFocus : function(){
16342         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16343             this.el.addClass(this.focusClass);
16344         }
16345         if(!this.hasFocus){
16346             this.hasFocus = true;
16347             this.startValue = this.getValue();
16348             this.fireEvent("focus", this);
16349         }
16350     },
16351
16352     beforeBlur : Roo.emptyFn,
16353
16354     // private
16355     onBlur : function(){
16356         this.beforeBlur();
16357         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16358             this.el.removeClass(this.focusClass);
16359         }
16360         this.hasFocus = false;
16361         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16362             this.validate();
16363         }
16364         var v = this.getValue();
16365         if(String(v) !== String(this.startValue)){
16366             this.fireEvent('change', this, v, this.startValue);
16367         }
16368         this.fireEvent("blur", this);
16369     },
16370
16371     /**
16372      * Returns whether or not the field value is currently valid
16373      * @param {Boolean} preventMark True to disable marking the field invalid
16374      * @return {Boolean} True if the value is valid, else false
16375      */
16376     isValid : function(preventMark){
16377         if(this.disabled){
16378             return true;
16379         }
16380         var restore = this.preventMark;
16381         this.preventMark = preventMark === true;
16382         var v = this.validateValue(this.processValue(this.getRawValue()));
16383         this.preventMark = restore;
16384         return v;
16385     },
16386
16387     /**
16388      * Validates the field value
16389      * @return {Boolean} True if the value is valid, else false
16390      */
16391     validate : function(){
16392         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16393             this.clearInvalid();
16394             return true;
16395         }
16396         return false;
16397     },
16398
16399     processValue : function(value){
16400         return value;
16401     },
16402
16403     // private
16404     // Subclasses should provide the validation implementation by overriding this
16405     validateValue : function(value){
16406         return true;
16407     },
16408
16409     /**
16410      * Mark this field as invalid
16411      * @param {String} msg The validation message
16412      */
16413     markInvalid : function(msg){
16414         if(!this.rendered || this.preventMark){ // not rendered
16415             return;
16416         }
16417         
16418         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16419         
16420         obj.el.addClass(this.invalidClass);
16421         msg = msg || this.invalidText;
16422         switch(this.msgTarget){
16423             case 'qtip':
16424                 obj.el.dom.qtip = msg;
16425                 obj.el.dom.qclass = 'x-form-invalid-tip';
16426                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16427                     Roo.QuickTips.enable();
16428                 }
16429                 break;
16430             case 'title':
16431                 this.el.dom.title = msg;
16432                 break;
16433             case 'under':
16434                 if(!this.errorEl){
16435                     var elp = this.el.findParent('.x-form-element', 5, true);
16436                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16437                     this.errorEl.setWidth(elp.getWidth(true)-20);
16438                 }
16439                 this.errorEl.update(msg);
16440                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16441                 break;
16442             case 'side':
16443                 if(!this.errorIcon){
16444                     var elp = this.el.findParent('.x-form-element', 5, true);
16445                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16446                 }
16447                 this.alignErrorIcon();
16448                 this.errorIcon.dom.qtip = msg;
16449                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16450                 this.errorIcon.show();
16451                 this.on('resize', this.alignErrorIcon, this);
16452                 break;
16453             default:
16454                 var t = Roo.getDom(this.msgTarget);
16455                 t.innerHTML = msg;
16456                 t.style.display = this.msgDisplay;
16457                 break;
16458         }
16459         this.fireEvent('invalid', this, msg);
16460     },
16461
16462     // private
16463     alignErrorIcon : function(){
16464         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16465     },
16466
16467     /**
16468      * Clear any invalid styles/messages for this field
16469      */
16470     clearInvalid : function(){
16471         if(!this.rendered || this.preventMark){ // not rendered
16472             return;
16473         }
16474         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16475         
16476         obj.el.removeClass(this.invalidClass);
16477         switch(this.msgTarget){
16478             case 'qtip':
16479                 obj.el.dom.qtip = '';
16480                 break;
16481             case 'title':
16482                 this.el.dom.title = '';
16483                 break;
16484             case 'under':
16485                 if(this.errorEl){
16486                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16487                 }
16488                 break;
16489             case 'side':
16490                 if(this.errorIcon){
16491                     this.errorIcon.dom.qtip = '';
16492                     this.errorIcon.hide();
16493                     this.un('resize', this.alignErrorIcon, this);
16494                 }
16495                 break;
16496             default:
16497                 var t = Roo.getDom(this.msgTarget);
16498                 t.innerHTML = '';
16499                 t.style.display = 'none';
16500                 break;
16501         }
16502         this.fireEvent('valid', this);
16503     },
16504
16505     /**
16506      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16507      * @return {Mixed} value The field value
16508      */
16509     getRawValue : function(){
16510         var v = this.el.getValue();
16511         
16512         return v;
16513     },
16514
16515     /**
16516      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16517      * @return {Mixed} value The field value
16518      */
16519     getValue : function(){
16520         var v = this.el.getValue();
16521          
16522         return v;
16523     },
16524
16525     /**
16526      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16527      * @param {Mixed} value The value to set
16528      */
16529     setRawValue : function(v){
16530         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16531     },
16532
16533     /**
16534      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16535      * @param {Mixed} value The value to set
16536      */
16537     setValue : function(v){
16538         this.value = v;
16539         if(this.rendered){
16540             this.el.dom.value = (v === null || v === undefined ? '' : v);
16541              this.validate();
16542         }
16543     },
16544
16545     adjustSize : function(w, h){
16546         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16547         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16548         return s;
16549     },
16550
16551     adjustWidth : function(tag, w){
16552         tag = tag.toLowerCase();
16553         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16554             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16555                 if(tag == 'input'){
16556                     return w + 2;
16557                 }
16558                 if(tag == 'textarea'){
16559                     return w-2;
16560                 }
16561             }else if(Roo.isOpera){
16562                 if(tag == 'input'){
16563                     return w + 2;
16564                 }
16565                 if(tag == 'textarea'){
16566                     return w-2;
16567                 }
16568             }
16569         }
16570         return w;
16571     }
16572 });
16573
16574
16575 // anything other than normal should be considered experimental
16576 Roo.form.Field.msgFx = {
16577     normal : {
16578         show: function(msgEl, f){
16579             msgEl.setDisplayed('block');
16580         },
16581
16582         hide : function(msgEl, f){
16583             msgEl.setDisplayed(false).update('');
16584         }
16585     },
16586
16587     slide : {
16588         show: function(msgEl, f){
16589             msgEl.slideIn('t', {stopFx:true});
16590         },
16591
16592         hide : function(msgEl, f){
16593             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16594         }
16595     },
16596
16597     slideRight : {
16598         show: function(msgEl, f){
16599             msgEl.fixDisplay();
16600             msgEl.alignTo(f.el, 'tl-tr');
16601             msgEl.slideIn('l', {stopFx:true});
16602         },
16603
16604         hide : function(msgEl, f){
16605             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16606         }
16607     }
16608 };/*
16609  * Based on:
16610  * Ext JS Library 1.1.1
16611  * Copyright(c) 2006-2007, Ext JS, LLC.
16612  *
16613  * Originally Released Under LGPL - original licence link has changed is not relivant.
16614  *
16615  * Fork - LGPL
16616  * <script type="text/javascript">
16617  */
16618  
16619
16620 /**
16621  * @class Roo.form.TextField
16622  * @extends Roo.form.Field
16623  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16624  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16625  * @constructor
16626  * Creates a new TextField
16627  * @param {Object} config Configuration options
16628  */
16629 Roo.form.TextField = function(config){
16630     Roo.form.TextField.superclass.constructor.call(this, config);
16631     this.addEvents({
16632         /**
16633          * @event autosize
16634          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16635          * according to the default logic, but this event provides a hook for the developer to apply additional
16636          * logic at runtime to resize the field if needed.
16637              * @param {Roo.form.Field} this This text field
16638              * @param {Number} width The new field width
16639              */
16640         autosize : true
16641     });
16642 };
16643
16644 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16645     /**
16646      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16647      */
16648     grow : false,
16649     /**
16650      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16651      */
16652     growMin : 30,
16653     /**
16654      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16655      */
16656     growMax : 800,
16657     /**
16658      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16659      */
16660     vtype : null,
16661     /**
16662      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16663      */
16664     maskRe : null,
16665     /**
16666      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16667      */
16668     disableKeyFilter : false,
16669     /**
16670      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16671      */
16672     allowBlank : true,
16673     /**
16674      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16675      */
16676     minLength : 0,
16677     /**
16678      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16679      */
16680     maxLength : Number.MAX_VALUE,
16681     /**
16682      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16683      */
16684     minLengthText : "The minimum length for this field is {0}",
16685     /**
16686      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16687      */
16688     maxLengthText : "The maximum length for this field is {0}",
16689     /**
16690      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16691      */
16692     selectOnFocus : false,
16693     /**
16694      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16695      */    
16696     allowLeadingSpace : false,
16697     /**
16698      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16699      */
16700     blankText : "This field is required",
16701     /**
16702      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16703      * If available, this function will be called only after the basic validators all return true, and will be passed the
16704      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16705      */
16706     validator : null,
16707     /**
16708      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16709      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16710      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16711      */
16712     regex : null,
16713     /**
16714      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16715      */
16716     regexText : "",
16717     /**
16718      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16719      */
16720     emptyText : null,
16721    
16722
16723     // private
16724     initEvents : function()
16725     {
16726         if (this.emptyText) {
16727             this.el.attr('placeholder', this.emptyText);
16728         }
16729         
16730         Roo.form.TextField.superclass.initEvents.call(this);
16731         if(this.validationEvent == 'keyup'){
16732             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16733             this.el.on('keyup', this.filterValidation, this);
16734         }
16735         else if(this.validationEvent !== false){
16736             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16737         }
16738         
16739         if(this.selectOnFocus){
16740             this.on("focus", this.preFocus, this);
16741         }
16742         if (!this.allowLeadingSpace) {
16743             this.on('blur', this.cleanLeadingSpace, this);
16744         }
16745         
16746         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16747             this.el.on("keypress", this.filterKeys, this);
16748         }
16749         if(this.grow){
16750             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16751             this.el.on("click", this.autoSize,  this);
16752         }
16753         if(this.el.is('input[type=password]') && Roo.isSafari){
16754             this.el.on('keydown', this.SafariOnKeyDown, this);
16755         }
16756     },
16757
16758     processValue : function(value){
16759         if(this.stripCharsRe){
16760             var newValue = value.replace(this.stripCharsRe, '');
16761             if(newValue !== value){
16762                 this.setRawValue(newValue);
16763                 return newValue;
16764             }
16765         }
16766         return value;
16767     },
16768
16769     filterValidation : function(e){
16770         if(!e.isNavKeyPress()){
16771             this.validationTask.delay(this.validationDelay);
16772         }
16773     },
16774
16775     // private
16776     onKeyUp : function(e){
16777         if(!e.isNavKeyPress()){
16778             this.autoSize();
16779         }
16780     },
16781     // private - clean the leading white space
16782     cleanLeadingSpace : function(e)
16783     {
16784         if ( this.inputType == 'file') {
16785             return;
16786         }
16787         
16788         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16789     },
16790     /**
16791      * Resets the current field value to the originally-loaded value and clears any validation messages.
16792      *  
16793      */
16794     reset : function(){
16795         Roo.form.TextField.superclass.reset.call(this);
16796        
16797     }, 
16798     // private
16799     preFocus : function(){
16800         
16801         if(this.selectOnFocus){
16802             this.el.dom.select();
16803         }
16804     },
16805
16806     
16807     // private
16808     filterKeys : function(e){
16809         var k = e.getKey();
16810         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16811             return;
16812         }
16813         var c = e.getCharCode(), cc = String.fromCharCode(c);
16814         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16815             return;
16816         }
16817         if(!this.maskRe.test(cc)){
16818             e.stopEvent();
16819         }
16820     },
16821
16822     setValue : function(v){
16823         
16824         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16825         
16826         this.autoSize();
16827     },
16828
16829     /**
16830      * Validates a value according to the field's validation rules and marks the field as invalid
16831      * if the validation fails
16832      * @param {Mixed} value The value to validate
16833      * @return {Boolean} True if the value is valid, else false
16834      */
16835     validateValue : function(value){
16836         if(value.length < 1)  { // if it's blank
16837              if(this.allowBlank){
16838                 this.clearInvalid();
16839                 return true;
16840              }else{
16841                 this.markInvalid(this.blankText);
16842                 return false;
16843              }
16844         }
16845         if(value.length < this.minLength){
16846             this.markInvalid(String.format(this.minLengthText, this.minLength));
16847             return false;
16848         }
16849         if(value.length > this.maxLength){
16850             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16851             return false;
16852         }
16853         if(this.vtype){
16854             var vt = Roo.form.VTypes;
16855             if(!vt[this.vtype](value, this)){
16856                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16857                 return false;
16858             }
16859         }
16860         if(typeof this.validator == "function"){
16861             var msg = this.validator(value);
16862             if(msg !== true){
16863                 this.markInvalid(msg);
16864                 return false;
16865             }
16866         }
16867         if(this.regex && !this.regex.test(value)){
16868             this.markInvalid(this.regexText);
16869             return false;
16870         }
16871         return true;
16872     },
16873
16874     /**
16875      * Selects text in this field
16876      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16877      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16878      */
16879     selectText : function(start, end){
16880         var v = this.getRawValue();
16881         if(v.length > 0){
16882             start = start === undefined ? 0 : start;
16883             end = end === undefined ? v.length : end;
16884             var d = this.el.dom;
16885             if(d.setSelectionRange){
16886                 d.setSelectionRange(start, end);
16887             }else if(d.createTextRange){
16888                 var range = d.createTextRange();
16889                 range.moveStart("character", start);
16890                 range.moveEnd("character", v.length-end);
16891                 range.select();
16892             }
16893         }
16894     },
16895
16896     /**
16897      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16898      * This only takes effect if grow = true, and fires the autosize event.
16899      */
16900     autoSize : function(){
16901         if(!this.grow || !this.rendered){
16902             return;
16903         }
16904         if(!this.metrics){
16905             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16906         }
16907         var el = this.el;
16908         var v = el.dom.value;
16909         var d = document.createElement('div');
16910         d.appendChild(document.createTextNode(v));
16911         v = d.innerHTML;
16912         d = null;
16913         v += "&#160;";
16914         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16915         this.el.setWidth(w);
16916         this.fireEvent("autosize", this, w);
16917     },
16918     
16919     // private
16920     SafariOnKeyDown : function(event)
16921     {
16922         // this is a workaround for a password hang bug on chrome/ webkit.
16923         
16924         var isSelectAll = false;
16925         
16926         if(this.el.dom.selectionEnd > 0){
16927             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16928         }
16929         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16930             event.preventDefault();
16931             this.setValue('');
16932             return;
16933         }
16934         
16935         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16936             
16937             event.preventDefault();
16938             // this is very hacky as keydown always get's upper case.
16939             
16940             var cc = String.fromCharCode(event.getCharCode());
16941             
16942             
16943             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16944             
16945         }
16946         
16947         
16948     }
16949 });/*
16950  * Based on:
16951  * Ext JS Library 1.1.1
16952  * Copyright(c) 2006-2007, Ext JS, LLC.
16953  *
16954  * Originally Released Under LGPL - original licence link has changed is not relivant.
16955  *
16956  * Fork - LGPL
16957  * <script type="text/javascript">
16958  */
16959  
16960 /**
16961  * @class Roo.form.Hidden
16962  * @extends Roo.form.TextField
16963  * Simple Hidden element used on forms 
16964  * 
16965  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16966  * 
16967  * @constructor
16968  * Creates a new Hidden form element.
16969  * @param {Object} config Configuration options
16970  */
16971
16972
16973
16974 // easy hidden field...
16975 Roo.form.Hidden = function(config){
16976     Roo.form.Hidden.superclass.constructor.call(this, config);
16977 };
16978   
16979 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16980     fieldLabel:      '',
16981     inputType:      'hidden',
16982     width:          50,
16983     allowBlank:     true,
16984     labelSeparator: '',
16985     hidden:         true,
16986     itemCls :       'x-form-item-display-none'
16987
16988
16989 });
16990
16991
16992 /*
16993  * Based on:
16994  * Ext JS Library 1.1.1
16995  * Copyright(c) 2006-2007, Ext JS, LLC.
16996  *
16997  * Originally Released Under LGPL - original licence link has changed is not relivant.
16998  *
16999  * Fork - LGPL
17000  * <script type="text/javascript">
17001  */
17002  
17003 /**
17004  * @class Roo.form.TriggerField
17005  * @extends Roo.form.TextField
17006  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17007  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17008  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17009  * for which you can provide a custom implementation.  For example:
17010  * <pre><code>
17011 var trigger = new Roo.form.TriggerField();
17012 trigger.onTriggerClick = myTriggerFn;
17013 trigger.applyTo('my-field');
17014 </code></pre>
17015  *
17016  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17017  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17018  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17019  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17020  * @constructor
17021  * Create a new TriggerField.
17022  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17023  * to the base TextField)
17024  */
17025 Roo.form.TriggerField = function(config){
17026     this.mimicing = false;
17027     Roo.form.TriggerField.superclass.constructor.call(this, config);
17028 };
17029
17030 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17031     /**
17032      * @cfg {String} triggerClass A CSS class to apply to the trigger
17033      */
17034     /**
17035      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17036      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17037      */
17038     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17039     /**
17040      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17041      */
17042     hideTrigger:false,
17043
17044     /** @cfg {Boolean} grow @hide */
17045     /** @cfg {Number} growMin @hide */
17046     /** @cfg {Number} growMax @hide */
17047
17048     /**
17049      * @hide 
17050      * @method
17051      */
17052     autoSize: Roo.emptyFn,
17053     // private
17054     monitorTab : true,
17055     // private
17056     deferHeight : true,
17057
17058     
17059     actionMode : 'wrap',
17060     // private
17061     onResize : function(w, h){
17062         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17063         if(typeof w == 'number'){
17064             var x = w - this.trigger.getWidth();
17065             this.el.setWidth(this.adjustWidth('input', x));
17066             this.trigger.setStyle('left', x+'px');
17067         }
17068     },
17069
17070     // private
17071     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17072
17073     // private
17074     getResizeEl : function(){
17075         return this.wrap;
17076     },
17077
17078     // private
17079     getPositionEl : function(){
17080         return this.wrap;
17081     },
17082
17083     // private
17084     alignErrorIcon : function(){
17085         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17086     },
17087
17088     // private
17089     onRender : function(ct, position){
17090         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17091         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17092         this.trigger = this.wrap.createChild(this.triggerConfig ||
17093                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17094         if(this.hideTrigger){
17095             this.trigger.setDisplayed(false);
17096         }
17097         this.initTrigger();
17098         if(!this.width){
17099             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17100         }
17101     },
17102
17103     // private
17104     initTrigger : function(){
17105         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17106         this.trigger.addClassOnOver('x-form-trigger-over');
17107         this.trigger.addClassOnClick('x-form-trigger-click');
17108     },
17109
17110     // private
17111     onDestroy : function(){
17112         if(this.trigger){
17113             this.trigger.removeAllListeners();
17114             this.trigger.remove();
17115         }
17116         if(this.wrap){
17117             this.wrap.remove();
17118         }
17119         Roo.form.TriggerField.superclass.onDestroy.call(this);
17120     },
17121
17122     // private
17123     onFocus : function(){
17124         Roo.form.TriggerField.superclass.onFocus.call(this);
17125         if(!this.mimicing){
17126             this.wrap.addClass('x-trigger-wrap-focus');
17127             this.mimicing = true;
17128             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17129             if(this.monitorTab){
17130                 this.el.on("keydown", this.checkTab, this);
17131             }
17132         }
17133     },
17134
17135     // private
17136     checkTab : function(e){
17137         if(e.getKey() == e.TAB){
17138             this.triggerBlur();
17139         }
17140     },
17141
17142     // private
17143     onBlur : function(){
17144         // do nothing
17145     },
17146
17147     // private
17148     mimicBlur : function(e, t){
17149         if(!this.wrap.contains(t) && this.validateBlur()){
17150             this.triggerBlur();
17151         }
17152     },
17153
17154     // private
17155     triggerBlur : function(){
17156         this.mimicing = false;
17157         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17158         if(this.monitorTab){
17159             this.el.un("keydown", this.checkTab, this);
17160         }
17161         this.wrap.removeClass('x-trigger-wrap-focus');
17162         Roo.form.TriggerField.superclass.onBlur.call(this);
17163     },
17164
17165     // private
17166     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17167     validateBlur : function(e, t){
17168         return true;
17169     },
17170
17171     // private
17172     onDisable : function(){
17173         Roo.form.TriggerField.superclass.onDisable.call(this);
17174         if(this.wrap){
17175             this.wrap.addClass('x-item-disabled');
17176         }
17177     },
17178
17179     // private
17180     onEnable : function(){
17181         Roo.form.TriggerField.superclass.onEnable.call(this);
17182         if(this.wrap){
17183             this.wrap.removeClass('x-item-disabled');
17184         }
17185     },
17186
17187     // private
17188     onShow : function(){
17189         var ae = this.getActionEl();
17190         
17191         if(ae){
17192             ae.dom.style.display = '';
17193             ae.dom.style.visibility = 'visible';
17194         }
17195     },
17196
17197     // private
17198     
17199     onHide : function(){
17200         var ae = this.getActionEl();
17201         ae.dom.style.display = 'none';
17202     },
17203
17204     /**
17205      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17206      * by an implementing function.
17207      * @method
17208      * @param {EventObject} e
17209      */
17210     onTriggerClick : Roo.emptyFn
17211 });
17212
17213 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17214 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17215 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17216 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17217     initComponent : function(){
17218         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17219
17220         this.triggerConfig = {
17221             tag:'span', cls:'x-form-twin-triggers', cn:[
17222             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17223             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17224         ]};
17225     },
17226
17227     getTrigger : function(index){
17228         return this.triggers[index];
17229     },
17230
17231     initTrigger : function(){
17232         var ts = this.trigger.select('.x-form-trigger', true);
17233         this.wrap.setStyle('overflow', 'hidden');
17234         var triggerField = this;
17235         ts.each(function(t, all, index){
17236             t.hide = function(){
17237                 var w = triggerField.wrap.getWidth();
17238                 this.dom.style.display = 'none';
17239                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17240             };
17241             t.show = function(){
17242                 var w = triggerField.wrap.getWidth();
17243                 this.dom.style.display = '';
17244                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17245             };
17246             var triggerIndex = 'Trigger'+(index+1);
17247
17248             if(this['hide'+triggerIndex]){
17249                 t.dom.style.display = 'none';
17250             }
17251             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17252             t.addClassOnOver('x-form-trigger-over');
17253             t.addClassOnClick('x-form-trigger-click');
17254         }, this);
17255         this.triggers = ts.elements;
17256     },
17257
17258     onTrigger1Click : Roo.emptyFn,
17259     onTrigger2Click : Roo.emptyFn
17260 });/*
17261  * Based on:
17262  * Ext JS Library 1.1.1
17263  * Copyright(c) 2006-2007, Ext JS, LLC.
17264  *
17265  * Originally Released Under LGPL - original licence link has changed is not relivant.
17266  *
17267  * Fork - LGPL
17268  * <script type="text/javascript">
17269  */
17270  
17271 /**
17272  * @class Roo.form.TextArea
17273  * @extends Roo.form.TextField
17274  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17275  * support for auto-sizing.
17276  * @constructor
17277  * Creates a new TextArea
17278  * @param {Object} config Configuration options
17279  */
17280 Roo.form.TextArea = function(config){
17281     Roo.form.TextArea.superclass.constructor.call(this, config);
17282     // these are provided exchanges for backwards compat
17283     // minHeight/maxHeight were replaced by growMin/growMax to be
17284     // compatible with TextField growing config values
17285     if(this.minHeight !== undefined){
17286         this.growMin = this.minHeight;
17287     }
17288     if(this.maxHeight !== undefined){
17289         this.growMax = this.maxHeight;
17290     }
17291 };
17292
17293 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17294     /**
17295      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17296      */
17297     growMin : 60,
17298     /**
17299      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17300      */
17301     growMax: 1000,
17302     /**
17303      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17304      * in the field (equivalent to setting overflow: hidden, defaults to false)
17305      */
17306     preventScrollbars: false,
17307     /**
17308      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17309      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17310      */
17311
17312     // private
17313     onRender : function(ct, position){
17314         if(!this.el){
17315             this.defaultAutoCreate = {
17316                 tag: "textarea",
17317                 style:"width:300px;height:60px;",
17318                 autocomplete: "new-password"
17319             };
17320         }
17321         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17322         if(this.grow){
17323             this.textSizeEl = Roo.DomHelper.append(document.body, {
17324                 tag: "pre", cls: "x-form-grow-sizer"
17325             });
17326             if(this.preventScrollbars){
17327                 this.el.setStyle("overflow", "hidden");
17328             }
17329             this.el.setHeight(this.growMin);
17330         }
17331     },
17332
17333     onDestroy : function(){
17334         if(this.textSizeEl){
17335             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17336         }
17337         Roo.form.TextArea.superclass.onDestroy.call(this);
17338     },
17339
17340     // private
17341     onKeyUp : function(e){
17342         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17343             this.autoSize();
17344         }
17345     },
17346
17347     /**
17348      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17349      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17350      */
17351     autoSize : function(){
17352         if(!this.grow || !this.textSizeEl){
17353             return;
17354         }
17355         var el = this.el;
17356         var v = el.dom.value;
17357         var ts = this.textSizeEl;
17358
17359         ts.innerHTML = '';
17360         ts.appendChild(document.createTextNode(v));
17361         v = ts.innerHTML;
17362
17363         Roo.fly(ts).setWidth(this.el.getWidth());
17364         if(v.length < 1){
17365             v = "&#160;&#160;";
17366         }else{
17367             if(Roo.isIE){
17368                 v = v.replace(/\n/g, '<p>&#160;</p>');
17369             }
17370             v += "&#160;\n&#160;";
17371         }
17372         ts.innerHTML = v;
17373         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17374         if(h != this.lastHeight){
17375             this.lastHeight = h;
17376             this.el.setHeight(h);
17377             this.fireEvent("autosize", this, h);
17378         }
17379     }
17380 });/*
17381  * Based on:
17382  * Ext JS Library 1.1.1
17383  * Copyright(c) 2006-2007, Ext JS, LLC.
17384  *
17385  * Originally Released Under LGPL - original licence link has changed is not relivant.
17386  *
17387  * Fork - LGPL
17388  * <script type="text/javascript">
17389  */
17390  
17391
17392 /**
17393  * @class Roo.form.NumberField
17394  * @extends Roo.form.TextField
17395  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17396  * @constructor
17397  * Creates a new NumberField
17398  * @param {Object} config Configuration options
17399  */
17400 Roo.form.NumberField = function(config){
17401     Roo.form.NumberField.superclass.constructor.call(this, config);
17402 };
17403
17404 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17405     /**
17406      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17407      */
17408     fieldClass: "x-form-field x-form-num-field",
17409     /**
17410      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17411      */
17412     allowDecimals : true,
17413     /**
17414      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17415      */
17416     decimalSeparator : ".",
17417     /**
17418      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17419      */
17420     decimalPrecision : 2,
17421     /**
17422      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17423      */
17424     allowNegative : true,
17425     /**
17426      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17427      */
17428     minValue : Number.NEGATIVE_INFINITY,
17429     /**
17430      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17431      */
17432     maxValue : Number.MAX_VALUE,
17433     /**
17434      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17435      */
17436     minText : "The minimum value for this field is {0}",
17437     /**
17438      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17439      */
17440     maxText : "The maximum value for this field is {0}",
17441     /**
17442      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17443      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17444      */
17445     nanText : "{0} is not a valid number",
17446
17447     // private
17448     initEvents : function(){
17449         Roo.form.NumberField.superclass.initEvents.call(this);
17450         var allowed = "0123456789";
17451         if(this.allowDecimals){
17452             allowed += this.decimalSeparator;
17453         }
17454         if(this.allowNegative){
17455             allowed += "-";
17456         }
17457         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17458         var keyPress = function(e){
17459             var k = e.getKey();
17460             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17461                 return;
17462             }
17463             var c = e.getCharCode();
17464             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17465                 e.stopEvent();
17466             }
17467         };
17468         this.el.on("keypress", keyPress, this);
17469     },
17470
17471     // private
17472     validateValue : function(value){
17473         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17474             return false;
17475         }
17476         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17477              return true;
17478         }
17479         var num = this.parseValue(value);
17480         if(isNaN(num)){
17481             this.markInvalid(String.format(this.nanText, value));
17482             return false;
17483         }
17484         if(num < this.minValue){
17485             this.markInvalid(String.format(this.minText, this.minValue));
17486             return false;
17487         }
17488         if(num > this.maxValue){
17489             this.markInvalid(String.format(this.maxText, this.maxValue));
17490             return false;
17491         }
17492         return true;
17493     },
17494
17495     getValue : function(){
17496         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17497     },
17498
17499     // private
17500     parseValue : function(value){
17501         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17502         return isNaN(value) ? '' : value;
17503     },
17504
17505     // private
17506     fixPrecision : function(value){
17507         var nan = isNaN(value);
17508         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17509             return nan ? '' : value;
17510         }
17511         return parseFloat(value).toFixed(this.decimalPrecision);
17512     },
17513
17514     setValue : function(v){
17515         v = this.fixPrecision(v);
17516         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17517     },
17518
17519     // private
17520     decimalPrecisionFcn : function(v){
17521         return Math.floor(v);
17522     },
17523
17524     beforeBlur : function(){
17525         var v = this.parseValue(this.getRawValue());
17526         if(v){
17527             this.setValue(v);
17528         }
17529     }
17530 });/*
17531  * Based on:
17532  * Ext JS Library 1.1.1
17533  * Copyright(c) 2006-2007, Ext JS, LLC.
17534  *
17535  * Originally Released Under LGPL - original licence link has changed is not relivant.
17536  *
17537  * Fork - LGPL
17538  * <script type="text/javascript">
17539  */
17540  
17541 /**
17542  * @class Roo.form.DateField
17543  * @extends Roo.form.TriggerField
17544  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17545 * @constructor
17546 * Create a new DateField
17547 * @param {Object} config
17548  */
17549 Roo.form.DateField = function(config)
17550 {
17551     Roo.form.DateField.superclass.constructor.call(this, config);
17552     
17553       this.addEvents({
17554          
17555         /**
17556          * @event select
17557          * Fires when a date is selected
17558              * @param {Roo.form.DateField} combo This combo box
17559              * @param {Date} date The date selected
17560              */
17561         'select' : true
17562          
17563     });
17564     
17565     
17566     if(typeof this.minValue == "string") {
17567         this.minValue = this.parseDate(this.minValue);
17568     }
17569     if(typeof this.maxValue == "string") {
17570         this.maxValue = this.parseDate(this.maxValue);
17571     }
17572     this.ddMatch = null;
17573     if(this.disabledDates){
17574         var dd = this.disabledDates;
17575         var re = "(?:";
17576         for(var i = 0; i < dd.length; i++){
17577             re += dd[i];
17578             if(i != dd.length-1) {
17579                 re += "|";
17580             }
17581         }
17582         this.ddMatch = new RegExp(re + ")");
17583     }
17584 };
17585
17586 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17587     /**
17588      * @cfg {String} format
17589      * The default date format string which can be overriden for localization support.  The format must be
17590      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17591      */
17592     format : "m/d/y",
17593     /**
17594      * @cfg {String} altFormats
17595      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17596      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17597      */
17598     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17599     /**
17600      * @cfg {Array} disabledDays
17601      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17602      */
17603     disabledDays : null,
17604     /**
17605      * @cfg {String} disabledDaysText
17606      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17607      */
17608     disabledDaysText : "Disabled",
17609     /**
17610      * @cfg {Array} disabledDates
17611      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17612      * expression so they are very powerful. Some examples:
17613      * <ul>
17614      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17615      * <li>["03/08", "09/16"] would disable those days for every year</li>
17616      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17617      * <li>["03/../2006"] would disable every day in March 2006</li>
17618      * <li>["^03"] would disable every day in every March</li>
17619      * </ul>
17620      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17621      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17622      */
17623     disabledDates : null,
17624     /**
17625      * @cfg {String} disabledDatesText
17626      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17627      */
17628     disabledDatesText : "Disabled",
17629         
17630         
17631         /**
17632      * @cfg {Date/String} zeroValue
17633      * if the date is less that this number, then the field is rendered as empty
17634      * default is 1800
17635      */
17636         zeroValue : '1800-01-01',
17637         
17638         
17639     /**
17640      * @cfg {Date/String} minValue
17641      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17642      * valid format (defaults to null).
17643      */
17644     minValue : null,
17645     /**
17646      * @cfg {Date/String} maxValue
17647      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17648      * valid format (defaults to null).
17649      */
17650     maxValue : null,
17651     /**
17652      * @cfg {String} minText
17653      * The error text to display when the date in the cell is before minValue (defaults to
17654      * 'The date in this field must be after {minValue}').
17655      */
17656     minText : "The date in this field must be equal to or after {0}",
17657     /**
17658      * @cfg {String} maxText
17659      * The error text to display when the date in the cell is after maxValue (defaults to
17660      * 'The date in this field must be before {maxValue}').
17661      */
17662     maxText : "The date in this field must be equal to or before {0}",
17663     /**
17664      * @cfg {String} invalidText
17665      * The error text to display when the date in the field is invalid (defaults to
17666      * '{value} is not a valid date - it must be in the format {format}').
17667      */
17668     invalidText : "{0} is not a valid date - it must be in the format {1}",
17669     /**
17670      * @cfg {String} triggerClass
17671      * An additional CSS class used to style the trigger button.  The trigger will always get the
17672      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17673      * which displays a calendar icon).
17674      */
17675     triggerClass : 'x-form-date-trigger',
17676     
17677
17678     /**
17679      * @cfg {Boolean} useIso
17680      * if enabled, then the date field will use a hidden field to store the 
17681      * real value as iso formated date. default (false)
17682      */ 
17683     useIso : false,
17684     /**
17685      * @cfg {String/Object} autoCreate
17686      * A DomHelper element spec, or true for a default element spec (defaults to
17687      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17688      */ 
17689     // private
17690     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17691     
17692     // private
17693     hiddenField: false,
17694     
17695     onRender : function(ct, position)
17696     {
17697         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17698         if (this.useIso) {
17699             //this.el.dom.removeAttribute('name'); 
17700             Roo.log("Changing name?");
17701             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17702             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17703                     'before', true);
17704             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17705             // prevent input submission
17706             this.hiddenName = this.name;
17707         }
17708             
17709             
17710     },
17711     
17712     // private
17713     validateValue : function(value)
17714     {
17715         value = this.formatDate(value);
17716         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17717             Roo.log('super failed');
17718             return false;
17719         }
17720         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17721              return true;
17722         }
17723         var svalue = value;
17724         value = this.parseDate(value);
17725         if(!value){
17726             Roo.log('parse date failed' + svalue);
17727             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17728             return false;
17729         }
17730         var time = value.getTime();
17731         if(this.minValue && time < this.minValue.getTime()){
17732             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17733             return false;
17734         }
17735         if(this.maxValue && time > this.maxValue.getTime()){
17736             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17737             return false;
17738         }
17739         if(this.disabledDays){
17740             var day = value.getDay();
17741             for(var i = 0; i < this.disabledDays.length; i++) {
17742                 if(day === this.disabledDays[i]){
17743                     this.markInvalid(this.disabledDaysText);
17744                     return false;
17745                 }
17746             }
17747         }
17748         var fvalue = this.formatDate(value);
17749         if(this.ddMatch && this.ddMatch.test(fvalue)){
17750             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17751             return false;
17752         }
17753         return true;
17754     },
17755
17756     // private
17757     // Provides logic to override the default TriggerField.validateBlur which just returns true
17758     validateBlur : function(){
17759         return !this.menu || !this.menu.isVisible();
17760     },
17761     
17762     getName: function()
17763     {
17764         // returns hidden if it's set..
17765         if (!this.rendered) {return ''};
17766         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17767         
17768     },
17769
17770     /**
17771      * Returns the current date value of the date field.
17772      * @return {Date} The date value
17773      */
17774     getValue : function(){
17775         
17776         return  this.hiddenField ?
17777                 this.hiddenField.value :
17778                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17779     },
17780
17781     /**
17782      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17783      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17784      * (the default format used is "m/d/y").
17785      * <br />Usage:
17786      * <pre><code>
17787 //All of these calls set the same date value (May 4, 2006)
17788
17789 //Pass a date object:
17790 var dt = new Date('5/4/06');
17791 dateField.setValue(dt);
17792
17793 //Pass a date string (default format):
17794 dateField.setValue('5/4/06');
17795
17796 //Pass a date string (custom format):
17797 dateField.format = 'Y-m-d';
17798 dateField.setValue('2006-5-4');
17799 </code></pre>
17800      * @param {String/Date} date The date or valid date string
17801      */
17802     setValue : function(date){
17803         if (this.hiddenField) {
17804             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17805         }
17806         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17807         // make sure the value field is always stored as a date..
17808         this.value = this.parseDate(date);
17809         
17810         
17811     },
17812
17813     // private
17814     parseDate : function(value){
17815                 
17816                 if (value instanceof Date) {
17817                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17818                                 return  '';
17819                         }
17820                         return value;
17821                 }
17822                 
17823                 
17824         if(!value || value instanceof Date){
17825             return value;
17826         }
17827         var v = Date.parseDate(value, this.format);
17828          if (!v && this.useIso) {
17829             v = Date.parseDate(value, 'Y-m-d');
17830         }
17831         if(!v && this.altFormats){
17832             if(!this.altFormatsArray){
17833                 this.altFormatsArray = this.altFormats.split("|");
17834             }
17835             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17836                 v = Date.parseDate(value, this.altFormatsArray[i]);
17837             }
17838         }
17839                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17840                         v = '';
17841                 }
17842         return v;
17843     },
17844
17845     // private
17846     formatDate : function(date, fmt){
17847         return (!date || !(date instanceof Date)) ?
17848                date : date.dateFormat(fmt || this.format);
17849     },
17850
17851     // private
17852     menuListeners : {
17853         select: function(m, d){
17854             
17855             this.setValue(d);
17856             this.fireEvent('select', this, d);
17857         },
17858         show : function(){ // retain focus styling
17859             this.onFocus();
17860         },
17861         hide : function(){
17862             this.focus.defer(10, this);
17863             var ml = this.menuListeners;
17864             this.menu.un("select", ml.select,  this);
17865             this.menu.un("show", ml.show,  this);
17866             this.menu.un("hide", ml.hide,  this);
17867         }
17868     },
17869
17870     // private
17871     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17872     onTriggerClick : function(){
17873         if(this.disabled){
17874             return;
17875         }
17876         if(this.menu == null){
17877             this.menu = new Roo.menu.DateMenu();
17878         }
17879         Roo.apply(this.menu.picker,  {
17880             showClear: this.allowBlank,
17881             minDate : this.minValue,
17882             maxDate : this.maxValue,
17883             disabledDatesRE : this.ddMatch,
17884             disabledDatesText : this.disabledDatesText,
17885             disabledDays : this.disabledDays,
17886             disabledDaysText : this.disabledDaysText,
17887             format : this.useIso ? 'Y-m-d' : this.format,
17888             minText : String.format(this.minText, this.formatDate(this.minValue)),
17889             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17890         });
17891         this.menu.on(Roo.apply({}, this.menuListeners, {
17892             scope:this
17893         }));
17894         this.menu.picker.setValue(this.getValue() || new Date());
17895         this.menu.show(this.el, "tl-bl?");
17896     },
17897
17898     beforeBlur : function(){
17899         var v = this.parseDate(this.getRawValue());
17900         if(v){
17901             this.setValue(v);
17902         }
17903     },
17904
17905     /*@
17906      * overide
17907      * 
17908      */
17909     isDirty : function() {
17910         if(this.disabled) {
17911             return false;
17912         }
17913         
17914         if(typeof(this.startValue) === 'undefined'){
17915             return false;
17916         }
17917         
17918         return String(this.getValue()) !== String(this.startValue);
17919         
17920     },
17921     // @overide
17922     cleanLeadingSpace : function(e)
17923     {
17924        return;
17925     }
17926     
17927 });/*
17928  * Based on:
17929  * Ext JS Library 1.1.1
17930  * Copyright(c) 2006-2007, Ext JS, LLC.
17931  *
17932  * Originally Released Under LGPL - original licence link has changed is not relivant.
17933  *
17934  * Fork - LGPL
17935  * <script type="text/javascript">
17936  */
17937  
17938 /**
17939  * @class Roo.form.MonthField
17940  * @extends Roo.form.TriggerField
17941  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17942 * @constructor
17943 * Create a new MonthField
17944 * @param {Object} config
17945  */
17946 Roo.form.MonthField = function(config){
17947     
17948     Roo.form.MonthField.superclass.constructor.call(this, config);
17949     
17950       this.addEvents({
17951          
17952         /**
17953          * @event select
17954          * Fires when a date is selected
17955              * @param {Roo.form.MonthFieeld} combo This combo box
17956              * @param {Date} date The date selected
17957              */
17958         'select' : true
17959          
17960     });
17961     
17962     
17963     if(typeof this.minValue == "string") {
17964         this.minValue = this.parseDate(this.minValue);
17965     }
17966     if(typeof this.maxValue == "string") {
17967         this.maxValue = this.parseDate(this.maxValue);
17968     }
17969     this.ddMatch = null;
17970     if(this.disabledDates){
17971         var dd = this.disabledDates;
17972         var re = "(?:";
17973         for(var i = 0; i < dd.length; i++){
17974             re += dd[i];
17975             if(i != dd.length-1) {
17976                 re += "|";
17977             }
17978         }
17979         this.ddMatch = new RegExp(re + ")");
17980     }
17981 };
17982
17983 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17984     /**
17985      * @cfg {String} format
17986      * The default date format string which can be overriden for localization support.  The format must be
17987      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17988      */
17989     format : "M Y",
17990     /**
17991      * @cfg {String} altFormats
17992      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17993      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17994      */
17995     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17996     /**
17997      * @cfg {Array} disabledDays
17998      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17999      */
18000     disabledDays : [0,1,2,3,4,5,6],
18001     /**
18002      * @cfg {String} disabledDaysText
18003      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18004      */
18005     disabledDaysText : "Disabled",
18006     /**
18007      * @cfg {Array} disabledDates
18008      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18009      * expression so they are very powerful. Some examples:
18010      * <ul>
18011      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18012      * <li>["03/08", "09/16"] would disable those days for every year</li>
18013      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18014      * <li>["03/../2006"] would disable every day in March 2006</li>
18015      * <li>["^03"] would disable every day in every March</li>
18016      * </ul>
18017      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18018      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18019      */
18020     disabledDates : null,
18021     /**
18022      * @cfg {String} disabledDatesText
18023      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18024      */
18025     disabledDatesText : "Disabled",
18026     /**
18027      * @cfg {Date/String} minValue
18028      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18029      * valid format (defaults to null).
18030      */
18031     minValue : null,
18032     /**
18033      * @cfg {Date/String} maxValue
18034      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18035      * valid format (defaults to null).
18036      */
18037     maxValue : null,
18038     /**
18039      * @cfg {String} minText
18040      * The error text to display when the date in the cell is before minValue (defaults to
18041      * 'The date in this field must be after {minValue}').
18042      */
18043     minText : "The date in this field must be equal to or after {0}",
18044     /**
18045      * @cfg {String} maxTextf
18046      * The error text to display when the date in the cell is after maxValue (defaults to
18047      * 'The date in this field must be before {maxValue}').
18048      */
18049     maxText : "The date in this field must be equal to or before {0}",
18050     /**
18051      * @cfg {String} invalidText
18052      * The error text to display when the date in the field is invalid (defaults to
18053      * '{value} is not a valid date - it must be in the format {format}').
18054      */
18055     invalidText : "{0} is not a valid date - it must be in the format {1}",
18056     /**
18057      * @cfg {String} triggerClass
18058      * An additional CSS class used to style the trigger button.  The trigger will always get the
18059      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18060      * which displays a calendar icon).
18061      */
18062     triggerClass : 'x-form-date-trigger',
18063     
18064
18065     /**
18066      * @cfg {Boolean} useIso
18067      * if enabled, then the date field will use a hidden field to store the 
18068      * real value as iso formated date. default (true)
18069      */ 
18070     useIso : true,
18071     /**
18072      * @cfg {String/Object} autoCreate
18073      * A DomHelper element spec, or true for a default element spec (defaults to
18074      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18075      */ 
18076     // private
18077     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18078     
18079     // private
18080     hiddenField: false,
18081     
18082     hideMonthPicker : false,
18083     
18084     onRender : function(ct, position)
18085     {
18086         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18087         if (this.useIso) {
18088             this.el.dom.removeAttribute('name'); 
18089             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18090                     'before', true);
18091             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18092             // prevent input submission
18093             this.hiddenName = this.name;
18094         }
18095             
18096             
18097     },
18098     
18099     // private
18100     validateValue : function(value)
18101     {
18102         value = this.formatDate(value);
18103         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18104             return false;
18105         }
18106         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18107              return true;
18108         }
18109         var svalue = value;
18110         value = this.parseDate(value);
18111         if(!value){
18112             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18113             return false;
18114         }
18115         var time = value.getTime();
18116         if(this.minValue && time < this.minValue.getTime()){
18117             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18118             return false;
18119         }
18120         if(this.maxValue && time > this.maxValue.getTime()){
18121             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18122             return false;
18123         }
18124         /*if(this.disabledDays){
18125             var day = value.getDay();
18126             for(var i = 0; i < this.disabledDays.length; i++) {
18127                 if(day === this.disabledDays[i]){
18128                     this.markInvalid(this.disabledDaysText);
18129                     return false;
18130                 }
18131             }
18132         }
18133         */
18134         var fvalue = this.formatDate(value);
18135         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18136             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18137             return false;
18138         }
18139         */
18140         return true;
18141     },
18142
18143     // private
18144     // Provides logic to override the default TriggerField.validateBlur which just returns true
18145     validateBlur : function(){
18146         return !this.menu || !this.menu.isVisible();
18147     },
18148
18149     /**
18150      * Returns the current date value of the date field.
18151      * @return {Date} The date value
18152      */
18153     getValue : function(){
18154         
18155         
18156         
18157         return  this.hiddenField ?
18158                 this.hiddenField.value :
18159                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18160     },
18161
18162     /**
18163      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18164      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18165      * (the default format used is "m/d/y").
18166      * <br />Usage:
18167      * <pre><code>
18168 //All of these calls set the same date value (May 4, 2006)
18169
18170 //Pass a date object:
18171 var dt = new Date('5/4/06');
18172 monthField.setValue(dt);
18173
18174 //Pass a date string (default format):
18175 monthField.setValue('5/4/06');
18176
18177 //Pass a date string (custom format):
18178 monthField.format = 'Y-m-d';
18179 monthField.setValue('2006-5-4');
18180 </code></pre>
18181      * @param {String/Date} date The date or valid date string
18182      */
18183     setValue : function(date){
18184         Roo.log('month setValue' + date);
18185         // can only be first of month..
18186         
18187         var val = this.parseDate(date);
18188         
18189         if (this.hiddenField) {
18190             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18191         }
18192         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18193         this.value = this.parseDate(date);
18194     },
18195
18196     // private
18197     parseDate : function(value){
18198         if(!value || value instanceof Date){
18199             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18200             return value;
18201         }
18202         var v = Date.parseDate(value, this.format);
18203         if (!v && this.useIso) {
18204             v = Date.parseDate(value, 'Y-m-d');
18205         }
18206         if (v) {
18207             // 
18208             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18209         }
18210         
18211         
18212         if(!v && this.altFormats){
18213             if(!this.altFormatsArray){
18214                 this.altFormatsArray = this.altFormats.split("|");
18215             }
18216             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18217                 v = Date.parseDate(value, this.altFormatsArray[i]);
18218             }
18219         }
18220         return v;
18221     },
18222
18223     // private
18224     formatDate : function(date, fmt){
18225         return (!date || !(date instanceof Date)) ?
18226                date : date.dateFormat(fmt || this.format);
18227     },
18228
18229     // private
18230     menuListeners : {
18231         select: function(m, d){
18232             this.setValue(d);
18233             this.fireEvent('select', this, d);
18234         },
18235         show : function(){ // retain focus styling
18236             this.onFocus();
18237         },
18238         hide : function(){
18239             this.focus.defer(10, this);
18240             var ml = this.menuListeners;
18241             this.menu.un("select", ml.select,  this);
18242             this.menu.un("show", ml.show,  this);
18243             this.menu.un("hide", ml.hide,  this);
18244         }
18245     },
18246     // private
18247     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18248     onTriggerClick : function(){
18249         if(this.disabled){
18250             return;
18251         }
18252         if(this.menu == null){
18253             this.menu = new Roo.menu.DateMenu();
18254            
18255         }
18256         
18257         Roo.apply(this.menu.picker,  {
18258             
18259             showClear: this.allowBlank,
18260             minDate : this.minValue,
18261             maxDate : this.maxValue,
18262             disabledDatesRE : this.ddMatch,
18263             disabledDatesText : this.disabledDatesText,
18264             
18265             format : this.useIso ? 'Y-m-d' : this.format,
18266             minText : String.format(this.minText, this.formatDate(this.minValue)),
18267             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18268             
18269         });
18270          this.menu.on(Roo.apply({}, this.menuListeners, {
18271             scope:this
18272         }));
18273        
18274         
18275         var m = this.menu;
18276         var p = m.picker;
18277         
18278         // hide month picker get's called when we called by 'before hide';
18279         
18280         var ignorehide = true;
18281         p.hideMonthPicker  = function(disableAnim){
18282             if (ignorehide) {
18283                 return;
18284             }
18285              if(this.monthPicker){
18286                 Roo.log("hideMonthPicker called");
18287                 if(disableAnim === true){
18288                     this.monthPicker.hide();
18289                 }else{
18290                     this.monthPicker.slideOut('t', {duration:.2});
18291                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18292                     p.fireEvent("select", this, this.value);
18293                     m.hide();
18294                 }
18295             }
18296         }
18297         
18298         Roo.log('picker set value');
18299         Roo.log(this.getValue());
18300         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18301         m.show(this.el, 'tl-bl?');
18302         ignorehide  = false;
18303         // this will trigger hideMonthPicker..
18304         
18305         
18306         // hidden the day picker
18307         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18308         
18309         
18310         
18311       
18312         
18313         p.showMonthPicker.defer(100, p);
18314     
18315         
18316        
18317     },
18318
18319     beforeBlur : function(){
18320         var v = this.parseDate(this.getRawValue());
18321         if(v){
18322             this.setValue(v);
18323         }
18324     }
18325
18326     /** @cfg {Boolean} grow @hide */
18327     /** @cfg {Number} growMin @hide */
18328     /** @cfg {Number} growMax @hide */
18329     /**
18330      * @hide
18331      * @method autoSize
18332      */
18333 });/*
18334  * Based on:
18335  * Ext JS Library 1.1.1
18336  * Copyright(c) 2006-2007, Ext JS, LLC.
18337  *
18338  * Originally Released Under LGPL - original licence link has changed is not relivant.
18339  *
18340  * Fork - LGPL
18341  * <script type="text/javascript">
18342  */
18343  
18344
18345 /**
18346  * @class Roo.form.ComboBox
18347  * @extends Roo.form.TriggerField
18348  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18349  * @constructor
18350  * Create a new ComboBox.
18351  * @param {Object} config Configuration options
18352  */
18353 Roo.form.ComboBox = function(config){
18354     Roo.form.ComboBox.superclass.constructor.call(this, config);
18355     this.addEvents({
18356         /**
18357          * @event expand
18358          * Fires when the dropdown list is expanded
18359              * @param {Roo.form.ComboBox} combo This combo box
18360              */
18361         'expand' : true,
18362         /**
18363          * @event collapse
18364          * Fires when the dropdown list is collapsed
18365              * @param {Roo.form.ComboBox} combo This combo box
18366              */
18367         'collapse' : true,
18368         /**
18369          * @event beforeselect
18370          * Fires before a list item is selected. Return false to cancel the selection.
18371              * @param {Roo.form.ComboBox} combo This combo box
18372              * @param {Roo.data.Record} record The data record returned from the underlying store
18373              * @param {Number} index The index of the selected item in the dropdown list
18374              */
18375         'beforeselect' : true,
18376         /**
18377          * @event select
18378          * Fires when a list item is selected
18379              * @param {Roo.form.ComboBox} combo This combo box
18380              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18381              * @param {Number} index The index of the selected item in the dropdown list
18382              */
18383         'select' : true,
18384         /**
18385          * @event beforequery
18386          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18387          * The event object passed has these properties:
18388              * @param {Roo.form.ComboBox} combo This combo box
18389              * @param {String} query The query
18390              * @param {Boolean} forceAll true to force "all" query
18391              * @param {Boolean} cancel true to cancel the query
18392              * @param {Object} e The query event object
18393              */
18394         'beforequery': true,
18395          /**
18396          * @event add
18397          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18398              * @param {Roo.form.ComboBox} combo This combo box
18399              */
18400         'add' : true,
18401         /**
18402          * @event edit
18403          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18404              * @param {Roo.form.ComboBox} combo This combo box
18405              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18406              */
18407         'edit' : true
18408         
18409         
18410     });
18411     if(this.transform){
18412         this.allowDomMove = false;
18413         var s = Roo.getDom(this.transform);
18414         if(!this.hiddenName){
18415             this.hiddenName = s.name;
18416         }
18417         if(!this.store){
18418             this.mode = 'local';
18419             var d = [], opts = s.options;
18420             for(var i = 0, len = opts.length;i < len; i++){
18421                 var o = opts[i];
18422                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18423                 if(o.selected) {
18424                     this.value = value;
18425                 }
18426                 d.push([value, o.text]);
18427             }
18428             this.store = new Roo.data.SimpleStore({
18429                 'id': 0,
18430                 fields: ['value', 'text'],
18431                 data : d
18432             });
18433             this.valueField = 'value';
18434             this.displayField = 'text';
18435         }
18436         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18437         if(!this.lazyRender){
18438             this.target = true;
18439             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18440             s.parentNode.removeChild(s); // remove it
18441             this.render(this.el.parentNode);
18442         }else{
18443             s.parentNode.removeChild(s); // remove it
18444         }
18445
18446     }
18447     if (this.store) {
18448         this.store = Roo.factory(this.store, Roo.data);
18449     }
18450     
18451     this.selectedIndex = -1;
18452     if(this.mode == 'local'){
18453         if(config.queryDelay === undefined){
18454             this.queryDelay = 10;
18455         }
18456         if(config.minChars === undefined){
18457             this.minChars = 0;
18458         }
18459     }
18460 };
18461
18462 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18463     /**
18464      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18465      */
18466     /**
18467      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18468      * rendering into an Roo.Editor, defaults to false)
18469      */
18470     /**
18471      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18472      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18473      */
18474     /**
18475      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18476      */
18477     /**
18478      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18479      * the dropdown list (defaults to undefined, with no header element)
18480      */
18481
18482      /**
18483      * @cfg {String/Roo.Template} tpl The template to use to render the output
18484      */
18485      
18486     // private
18487     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18488     /**
18489      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18490      */
18491     listWidth: undefined,
18492     /**
18493      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18494      * mode = 'remote' or 'text' if mode = 'local')
18495      */
18496     displayField: undefined,
18497     /**
18498      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18499      * mode = 'remote' or 'value' if mode = 'local'). 
18500      * Note: use of a valueField requires the user make a selection
18501      * in order for a value to be mapped.
18502      */
18503     valueField: undefined,
18504     
18505     
18506     /**
18507      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18508      * field's data value (defaults to the underlying DOM element's name)
18509      */
18510     hiddenName: undefined,
18511     /**
18512      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18513      */
18514     listClass: '',
18515     /**
18516      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18517      */
18518     selectedClass: 'x-combo-selected',
18519     /**
18520      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18521      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18522      * which displays a downward arrow icon).
18523      */
18524     triggerClass : 'x-form-arrow-trigger',
18525     /**
18526      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18527      */
18528     shadow:'sides',
18529     /**
18530      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18531      * anchor positions (defaults to 'tl-bl')
18532      */
18533     listAlign: 'tl-bl?',
18534     /**
18535      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18536      */
18537     maxHeight: 300,
18538     /**
18539      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18540      * query specified by the allQuery config option (defaults to 'query')
18541      */
18542     triggerAction: 'query',
18543     /**
18544      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18545      * (defaults to 4, does not apply if editable = false)
18546      */
18547     minChars : 4,
18548     /**
18549      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18550      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18551      */
18552     typeAhead: false,
18553     /**
18554      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18555      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18556      */
18557     queryDelay: 500,
18558     /**
18559      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18560      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18561      */
18562     pageSize: 0,
18563     /**
18564      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18565      * when editable = true (defaults to false)
18566      */
18567     selectOnFocus:false,
18568     /**
18569      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18570      */
18571     queryParam: 'query',
18572     /**
18573      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18574      * when mode = 'remote' (defaults to 'Loading...')
18575      */
18576     loadingText: 'Loading...',
18577     /**
18578      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18579      */
18580     resizable: false,
18581     /**
18582      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18583      */
18584     handleHeight : 8,
18585     /**
18586      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18587      * traditional select (defaults to true)
18588      */
18589     editable: true,
18590     /**
18591      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18592      */
18593     allQuery: '',
18594     /**
18595      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18596      */
18597     mode: 'remote',
18598     /**
18599      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18600      * listWidth has a higher value)
18601      */
18602     minListWidth : 70,
18603     /**
18604      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18605      * allow the user to set arbitrary text into the field (defaults to false)
18606      */
18607     forceSelection:false,
18608     /**
18609      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18610      * if typeAhead = true (defaults to 250)
18611      */
18612     typeAheadDelay : 250,
18613     /**
18614      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18615      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18616      */
18617     valueNotFoundText : undefined,
18618     /**
18619      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18620      */
18621     blockFocus : false,
18622     
18623     /**
18624      * @cfg {Boolean} disableClear Disable showing of clear button.
18625      */
18626     disableClear : false,
18627     /**
18628      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18629      */
18630     alwaysQuery : false,
18631     
18632     //private
18633     addicon : false,
18634     editicon: false,
18635     
18636     // element that contains real text value.. (when hidden is used..)
18637      
18638     // private
18639     onRender : function(ct, position)
18640     {
18641         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18642         
18643                 if(this.hiddenName){
18644             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18645                     'before', true);
18646             this.hiddenField.value =
18647                 this.hiddenValue !== undefined ? this.hiddenValue :
18648                 this.value !== undefined ? this.value : '';
18649
18650             // prevent input submission
18651             this.el.dom.removeAttribute('name');
18652              
18653              
18654         }
18655         
18656         if(Roo.isGecko){
18657             this.el.dom.setAttribute('autocomplete', 'off');
18658         }
18659
18660         var cls = 'x-combo-list';
18661
18662         this.list = new Roo.Layer({
18663             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18664         });
18665
18666         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18667         this.list.setWidth(lw);
18668         this.list.swallowEvent('mousewheel');
18669         this.assetHeight = 0;
18670
18671         if(this.title){
18672             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18673             this.assetHeight += this.header.getHeight();
18674         }
18675
18676         this.innerList = this.list.createChild({cls:cls+'-inner'});
18677         this.innerList.on('mouseover', this.onViewOver, this);
18678         this.innerList.on('mousemove', this.onViewMove, this);
18679         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18680         
18681         if(this.allowBlank && !this.pageSize && !this.disableClear){
18682             this.footer = this.list.createChild({cls:cls+'-ft'});
18683             this.pageTb = new Roo.Toolbar(this.footer);
18684            
18685         }
18686         if(this.pageSize){
18687             this.footer = this.list.createChild({cls:cls+'-ft'});
18688             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18689                     {pageSize: this.pageSize});
18690             
18691         }
18692         
18693         if (this.pageTb && this.allowBlank && !this.disableClear) {
18694             var _this = this;
18695             this.pageTb.add(new Roo.Toolbar.Fill(), {
18696                 cls: 'x-btn-icon x-btn-clear',
18697                 text: '&#160;',
18698                 handler: function()
18699                 {
18700                     _this.collapse();
18701                     _this.clearValue();
18702                     _this.onSelect(false, -1);
18703                 }
18704             });
18705         }
18706         if (this.footer) {
18707             this.assetHeight += this.footer.getHeight();
18708         }
18709         
18710
18711         if(!this.tpl){
18712             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18713         }
18714
18715         this.view = new Roo.View(this.innerList, this.tpl, {
18716             singleSelect:true,
18717             store: this.store,
18718             selectedClass: this.selectedClass
18719         });
18720
18721         this.view.on('click', this.onViewClick, this);
18722
18723         this.store.on('beforeload', this.onBeforeLoad, this);
18724         this.store.on('load', this.onLoad, this);
18725         this.store.on('loadexception', this.onLoadException, this);
18726
18727         if(this.resizable){
18728             this.resizer = new Roo.Resizable(this.list,  {
18729                pinned:true, handles:'se'
18730             });
18731             this.resizer.on('resize', function(r, w, h){
18732                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18733                 this.listWidth = w;
18734                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18735                 this.restrictHeight();
18736             }, this);
18737             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18738         }
18739         if(!this.editable){
18740             this.editable = true;
18741             this.setEditable(false);
18742         }  
18743         
18744         
18745         if (typeof(this.events.add.listeners) != 'undefined') {
18746             
18747             this.addicon = this.wrap.createChild(
18748                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18749        
18750             this.addicon.on('click', function(e) {
18751                 this.fireEvent('add', this);
18752             }, this);
18753         }
18754         if (typeof(this.events.edit.listeners) != 'undefined') {
18755             
18756             this.editicon = this.wrap.createChild(
18757                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18758             if (this.addicon) {
18759                 this.editicon.setStyle('margin-left', '40px');
18760             }
18761             this.editicon.on('click', function(e) {
18762                 
18763                 // we fire even  if inothing is selected..
18764                 this.fireEvent('edit', this, this.lastData );
18765                 
18766             }, this);
18767         }
18768         
18769         
18770         
18771     },
18772
18773     // private
18774     initEvents : function(){
18775         Roo.form.ComboBox.superclass.initEvents.call(this);
18776
18777         this.keyNav = new Roo.KeyNav(this.el, {
18778             "up" : function(e){
18779                 this.inKeyMode = true;
18780                 this.selectPrev();
18781             },
18782
18783             "down" : function(e){
18784                 if(!this.isExpanded()){
18785                     this.onTriggerClick();
18786                 }else{
18787                     this.inKeyMode = true;
18788                     this.selectNext();
18789                 }
18790             },
18791
18792             "enter" : function(e){
18793                 this.onViewClick();
18794                 //return true;
18795             },
18796
18797             "esc" : function(e){
18798                 this.collapse();
18799             },
18800
18801             "tab" : function(e){
18802                 this.onViewClick(false);
18803                 this.fireEvent("specialkey", this, e);
18804                 return true;
18805             },
18806
18807             scope : this,
18808
18809             doRelay : function(foo, bar, hname){
18810                 if(hname == 'down' || this.scope.isExpanded()){
18811                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18812                 }
18813                 return true;
18814             },
18815
18816             forceKeyDown: true
18817         });
18818         this.queryDelay = Math.max(this.queryDelay || 10,
18819                 this.mode == 'local' ? 10 : 250);
18820         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18821         if(this.typeAhead){
18822             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18823         }
18824         if(this.editable !== false){
18825             this.el.on("keyup", this.onKeyUp, this);
18826         }
18827         if(this.forceSelection){
18828             this.on('blur', this.doForce, this);
18829         }
18830     },
18831
18832     onDestroy : function(){
18833         if(this.view){
18834             this.view.setStore(null);
18835             this.view.el.removeAllListeners();
18836             this.view.el.remove();
18837             this.view.purgeListeners();
18838         }
18839         if(this.list){
18840             this.list.destroy();
18841         }
18842         if(this.store){
18843             this.store.un('beforeload', this.onBeforeLoad, this);
18844             this.store.un('load', this.onLoad, this);
18845             this.store.un('loadexception', this.onLoadException, this);
18846         }
18847         Roo.form.ComboBox.superclass.onDestroy.call(this);
18848     },
18849
18850     // private
18851     fireKey : function(e){
18852         if(e.isNavKeyPress() && !this.list.isVisible()){
18853             this.fireEvent("specialkey", this, e);
18854         }
18855     },
18856
18857     // private
18858     onResize: function(w, h){
18859         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18860         
18861         if(typeof w != 'number'){
18862             // we do not handle it!?!?
18863             return;
18864         }
18865         var tw = this.trigger.getWidth();
18866         tw += this.addicon ? this.addicon.getWidth() : 0;
18867         tw += this.editicon ? this.editicon.getWidth() : 0;
18868         var x = w - tw;
18869         this.el.setWidth( this.adjustWidth('input', x));
18870             
18871         this.trigger.setStyle('left', x+'px');
18872         
18873         if(this.list && this.listWidth === undefined){
18874             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18875             this.list.setWidth(lw);
18876             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18877         }
18878         
18879     
18880         
18881     },
18882
18883     /**
18884      * Allow or prevent the user from directly editing the field text.  If false is passed,
18885      * the user will only be able to select from the items defined in the dropdown list.  This method
18886      * is the runtime equivalent of setting the 'editable' config option at config time.
18887      * @param {Boolean} value True to allow the user to directly edit the field text
18888      */
18889     setEditable : function(value){
18890         if(value == this.editable){
18891             return;
18892         }
18893         this.editable = value;
18894         if(!value){
18895             this.el.dom.setAttribute('readOnly', true);
18896             this.el.on('mousedown', this.onTriggerClick,  this);
18897             this.el.addClass('x-combo-noedit');
18898         }else{
18899             this.el.dom.setAttribute('readOnly', false);
18900             this.el.un('mousedown', this.onTriggerClick,  this);
18901             this.el.removeClass('x-combo-noedit');
18902         }
18903     },
18904
18905     // private
18906     onBeforeLoad : function(){
18907         if(!this.hasFocus){
18908             return;
18909         }
18910         this.innerList.update(this.loadingText ?
18911                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18912         this.restrictHeight();
18913         this.selectedIndex = -1;
18914     },
18915
18916     // private
18917     onLoad : function(){
18918         if(!this.hasFocus){
18919             return;
18920         }
18921         if(this.store.getCount() > 0){
18922             this.expand();
18923             this.restrictHeight();
18924             if(this.lastQuery == this.allQuery){
18925                 if(this.editable){
18926                     this.el.dom.select();
18927                 }
18928                 if(!this.selectByValue(this.value, true)){
18929                     this.select(0, true);
18930                 }
18931             }else{
18932                 this.selectNext();
18933                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18934                     this.taTask.delay(this.typeAheadDelay);
18935                 }
18936             }
18937         }else{
18938             this.onEmptyResults();
18939         }
18940         //this.el.focus();
18941     },
18942     // private
18943     onLoadException : function()
18944     {
18945         this.collapse();
18946         Roo.log(this.store.reader.jsonData);
18947         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18948             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18949         }
18950         
18951         
18952     },
18953     // private
18954     onTypeAhead : function(){
18955         if(this.store.getCount() > 0){
18956             var r = this.store.getAt(0);
18957             var newValue = r.data[this.displayField];
18958             var len = newValue.length;
18959             var selStart = this.getRawValue().length;
18960             if(selStart != len){
18961                 this.setRawValue(newValue);
18962                 this.selectText(selStart, newValue.length);
18963             }
18964         }
18965     },
18966
18967     // private
18968     onSelect : function(record, index){
18969         if(this.fireEvent('beforeselect', this, record, index) !== false){
18970             this.setFromData(index > -1 ? record.data : false);
18971             this.collapse();
18972             this.fireEvent('select', this, record, index);
18973         }
18974     },
18975
18976     /**
18977      * Returns the currently selected field value or empty string if no value is set.
18978      * @return {String} value The selected value
18979      */
18980     getValue : function(){
18981         if(this.valueField){
18982             return typeof this.value != 'undefined' ? this.value : '';
18983         }
18984         return Roo.form.ComboBox.superclass.getValue.call(this);
18985     },
18986
18987     /**
18988      * Clears any text/value currently set in the field
18989      */
18990     clearValue : function(){
18991         if(this.hiddenField){
18992             this.hiddenField.value = '';
18993         }
18994         this.value = '';
18995         this.setRawValue('');
18996         this.lastSelectionText = '';
18997         
18998     },
18999
19000     /**
19001      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19002      * will be displayed in the field.  If the value does not match the data value of an existing item,
19003      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19004      * Otherwise the field will be blank (although the value will still be set).
19005      * @param {String} value The value to match
19006      */
19007     setValue : function(v){
19008         var text = v;
19009         if(this.valueField){
19010             var r = this.findRecord(this.valueField, v);
19011             if(r){
19012                 text = r.data[this.displayField];
19013             }else if(this.valueNotFoundText !== undefined){
19014                 text = this.valueNotFoundText;
19015             }
19016         }
19017         this.lastSelectionText = text;
19018         if(this.hiddenField){
19019             this.hiddenField.value = v;
19020         }
19021         Roo.form.ComboBox.superclass.setValue.call(this, text);
19022         this.value = v;
19023     },
19024     /**
19025      * @property {Object} the last set data for the element
19026      */
19027     
19028     lastData : false,
19029     /**
19030      * Sets the value of the field based on a object which is related to the record format for the store.
19031      * @param {Object} value the value to set as. or false on reset?
19032      */
19033     setFromData : function(o){
19034         var dv = ''; // display value
19035         var vv = ''; // value value..
19036         this.lastData = o;
19037         if (this.displayField) {
19038             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19039         } else {
19040             // this is an error condition!!!
19041             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19042         }
19043         
19044         if(this.valueField){
19045             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19046         }
19047         if(this.hiddenField){
19048             this.hiddenField.value = vv;
19049             
19050             this.lastSelectionText = dv;
19051             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19052             this.value = vv;
19053             return;
19054         }
19055         // no hidden field.. - we store the value in 'value', but still display
19056         // display field!!!!
19057         this.lastSelectionText = dv;
19058         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19059         this.value = vv;
19060         
19061         
19062     },
19063     // private
19064     reset : function(){
19065         // overridden so that last data is reset..
19066         this.setValue(this.resetValue);
19067         this.originalValue = this.getValue();
19068         this.clearInvalid();
19069         this.lastData = false;
19070         if (this.view) {
19071             this.view.clearSelections();
19072         }
19073     },
19074     // private
19075     findRecord : function(prop, value){
19076         var record;
19077         if(this.store.getCount() > 0){
19078             this.store.each(function(r){
19079                 if(r.data[prop] == value){
19080                     record = r;
19081                     return false;
19082                 }
19083                 return true;
19084             });
19085         }
19086         return record;
19087     },
19088     
19089     getName: function()
19090     {
19091         // returns hidden if it's set..
19092         if (!this.rendered) {return ''};
19093         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19094         
19095     },
19096     // private
19097     onViewMove : function(e, t){
19098         this.inKeyMode = false;
19099     },
19100
19101     // private
19102     onViewOver : function(e, t){
19103         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19104             return;
19105         }
19106         var item = this.view.findItemFromChild(t);
19107         if(item){
19108             var index = this.view.indexOf(item);
19109             this.select(index, false);
19110         }
19111     },
19112
19113     // private
19114     onViewClick : function(doFocus)
19115     {
19116         var index = this.view.getSelectedIndexes()[0];
19117         var r = this.store.getAt(index);
19118         if(r){
19119             this.onSelect(r, index);
19120         }
19121         if(doFocus !== false && !this.blockFocus){
19122             this.el.focus();
19123         }
19124     },
19125
19126     // private
19127     restrictHeight : function(){
19128         this.innerList.dom.style.height = '';
19129         var inner = this.innerList.dom;
19130         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19131         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19132         this.list.beginUpdate();
19133         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19134         this.list.alignTo(this.el, this.listAlign);
19135         this.list.endUpdate();
19136     },
19137
19138     // private
19139     onEmptyResults : function(){
19140         this.collapse();
19141     },
19142
19143     /**
19144      * Returns true if the dropdown list is expanded, else false.
19145      */
19146     isExpanded : function(){
19147         return this.list.isVisible();
19148     },
19149
19150     /**
19151      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19152      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19153      * @param {String} value The data value of the item to select
19154      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19155      * selected item if it is not currently in view (defaults to true)
19156      * @return {Boolean} True if the value matched an item in the list, else false
19157      */
19158     selectByValue : function(v, scrollIntoView){
19159         if(v !== undefined && v !== null){
19160             var r = this.findRecord(this.valueField || this.displayField, v);
19161             if(r){
19162                 this.select(this.store.indexOf(r), scrollIntoView);
19163                 return true;
19164             }
19165         }
19166         return false;
19167     },
19168
19169     /**
19170      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19171      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19172      * @param {Number} index The zero-based index of the list item to select
19173      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19174      * selected item if it is not currently in view (defaults to true)
19175      */
19176     select : function(index, scrollIntoView){
19177         this.selectedIndex = index;
19178         this.view.select(index);
19179         if(scrollIntoView !== false){
19180             var el = this.view.getNode(index);
19181             if(el){
19182                 this.innerList.scrollChildIntoView(el, false);
19183             }
19184         }
19185     },
19186
19187     // private
19188     selectNext : function(){
19189         var ct = this.store.getCount();
19190         if(ct > 0){
19191             if(this.selectedIndex == -1){
19192                 this.select(0);
19193             }else if(this.selectedIndex < ct-1){
19194                 this.select(this.selectedIndex+1);
19195             }
19196         }
19197     },
19198
19199     // private
19200     selectPrev : function(){
19201         var ct = this.store.getCount();
19202         if(ct > 0){
19203             if(this.selectedIndex == -1){
19204                 this.select(0);
19205             }else if(this.selectedIndex != 0){
19206                 this.select(this.selectedIndex-1);
19207             }
19208         }
19209     },
19210
19211     // private
19212     onKeyUp : function(e){
19213         if(this.editable !== false && !e.isSpecialKey()){
19214             this.lastKey = e.getKey();
19215             this.dqTask.delay(this.queryDelay);
19216         }
19217     },
19218
19219     // private
19220     validateBlur : function(){
19221         return !this.list || !this.list.isVisible();   
19222     },
19223
19224     // private
19225     initQuery : function(){
19226         this.doQuery(this.getRawValue());
19227     },
19228
19229     // private
19230     doForce : function(){
19231         if(this.el.dom.value.length > 0){
19232             this.el.dom.value =
19233                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19234              
19235         }
19236     },
19237
19238     /**
19239      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19240      * query allowing the query action to be canceled if needed.
19241      * @param {String} query The SQL query to execute
19242      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19243      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19244      * saved in the current store (defaults to false)
19245      */
19246     doQuery : function(q, forceAll){
19247         if(q === undefined || q === null){
19248             q = '';
19249         }
19250         var qe = {
19251             query: q,
19252             forceAll: forceAll,
19253             combo: this,
19254             cancel:false
19255         };
19256         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19257             return false;
19258         }
19259         q = qe.query;
19260         forceAll = qe.forceAll;
19261         if(forceAll === true || (q.length >= this.minChars)){
19262             if(this.lastQuery != q || this.alwaysQuery){
19263                 this.lastQuery = q;
19264                 if(this.mode == 'local'){
19265                     this.selectedIndex = -1;
19266                     if(forceAll){
19267                         this.store.clearFilter();
19268                     }else{
19269                         this.store.filter(this.displayField, q);
19270                     }
19271                     this.onLoad();
19272                 }else{
19273                     this.store.baseParams[this.queryParam] = q;
19274                     this.store.load({
19275                         params: this.getParams(q)
19276                     });
19277                     this.expand();
19278                 }
19279             }else{
19280                 this.selectedIndex = -1;
19281                 this.onLoad();   
19282             }
19283         }
19284     },
19285
19286     // private
19287     getParams : function(q){
19288         var p = {};
19289         //p[this.queryParam] = q;
19290         if(this.pageSize){
19291             p.start = 0;
19292             p.limit = this.pageSize;
19293         }
19294         return p;
19295     },
19296
19297     /**
19298      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19299      */
19300     collapse : function(){
19301         if(!this.isExpanded()){
19302             return;
19303         }
19304         this.list.hide();
19305         Roo.get(document).un('mousedown', this.collapseIf, this);
19306         Roo.get(document).un('mousewheel', this.collapseIf, this);
19307         if (!this.editable) {
19308             Roo.get(document).un('keydown', this.listKeyPress, this);
19309         }
19310         this.fireEvent('collapse', this);
19311     },
19312
19313     // private
19314     collapseIf : function(e){
19315         if(!e.within(this.wrap) && !e.within(this.list)){
19316             this.collapse();
19317         }
19318     },
19319
19320     /**
19321      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19322      */
19323     expand : function(){
19324         if(this.isExpanded() || !this.hasFocus){
19325             return;
19326         }
19327         this.list.alignTo(this.el, this.listAlign);
19328         this.list.show();
19329         Roo.get(document).on('mousedown', this.collapseIf, this);
19330         Roo.get(document).on('mousewheel', this.collapseIf, this);
19331         if (!this.editable) {
19332             Roo.get(document).on('keydown', this.listKeyPress, this);
19333         }
19334         
19335         this.fireEvent('expand', this);
19336     },
19337
19338     // private
19339     // Implements the default empty TriggerField.onTriggerClick function
19340     onTriggerClick : function(){
19341         if(this.disabled){
19342             return;
19343         }
19344         if(this.isExpanded()){
19345             this.collapse();
19346             if (!this.blockFocus) {
19347                 this.el.focus();
19348             }
19349             
19350         }else {
19351             this.hasFocus = true;
19352             if(this.triggerAction == 'all') {
19353                 this.doQuery(this.allQuery, true);
19354             } else {
19355                 this.doQuery(this.getRawValue());
19356             }
19357             if (!this.blockFocus) {
19358                 this.el.focus();
19359             }
19360         }
19361     },
19362     listKeyPress : function(e)
19363     {
19364         //Roo.log('listkeypress');
19365         // scroll to first matching element based on key pres..
19366         if (e.isSpecialKey()) {
19367             return false;
19368         }
19369         var k = String.fromCharCode(e.getKey()).toUpperCase();
19370         //Roo.log(k);
19371         var match  = false;
19372         var csel = this.view.getSelectedNodes();
19373         var cselitem = false;
19374         if (csel.length) {
19375             var ix = this.view.indexOf(csel[0]);
19376             cselitem  = this.store.getAt(ix);
19377             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19378                 cselitem = false;
19379             }
19380             
19381         }
19382         
19383         this.store.each(function(v) { 
19384             if (cselitem) {
19385                 // start at existing selection.
19386                 if (cselitem.id == v.id) {
19387                     cselitem = false;
19388                 }
19389                 return;
19390             }
19391                 
19392             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19393                 match = this.store.indexOf(v);
19394                 return false;
19395             }
19396         }, this);
19397         
19398         if (match === false) {
19399             return true; // no more action?
19400         }
19401         // scroll to?
19402         this.view.select(match);
19403         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19404         sn.scrollIntoView(sn.dom.parentNode, false);
19405     } 
19406
19407     /** 
19408     * @cfg {Boolean} grow 
19409     * @hide 
19410     */
19411     /** 
19412     * @cfg {Number} growMin 
19413     * @hide 
19414     */
19415     /** 
19416     * @cfg {Number} growMax 
19417     * @hide 
19418     */
19419     /**
19420      * @hide
19421      * @method autoSize
19422      */
19423 });/*
19424  * Copyright(c) 2010-2012, Roo J Solutions Limited
19425  *
19426  * Licence LGPL
19427  *
19428  */
19429
19430 /**
19431  * @class Roo.form.ComboBoxArray
19432  * @extends Roo.form.TextField
19433  * A facebook style adder... for lists of email / people / countries  etc...
19434  * pick multiple items from a combo box, and shows each one.
19435  *
19436  *  Fred [x]  Brian [x]  [Pick another |v]
19437  *
19438  *
19439  *  For this to work: it needs various extra information
19440  *    - normal combo problay has
19441  *      name, hiddenName
19442  *    + displayField, valueField
19443  *
19444  *    For our purpose...
19445  *
19446  *
19447  *   If we change from 'extends' to wrapping...
19448  *   
19449  *  
19450  *
19451  
19452  
19453  * @constructor
19454  * Create a new ComboBoxArray.
19455  * @param {Object} config Configuration options
19456  */
19457  
19458
19459 Roo.form.ComboBoxArray = function(config)
19460 {
19461     this.addEvents({
19462         /**
19463          * @event beforeremove
19464          * Fires before remove the value from the list
19465              * @param {Roo.form.ComboBoxArray} _self This combo box array
19466              * @param {Roo.form.ComboBoxArray.Item} item removed item
19467              */
19468         'beforeremove' : true,
19469         /**
19470          * @event remove
19471          * Fires when remove the value from the list
19472              * @param {Roo.form.ComboBoxArray} _self This combo box array
19473              * @param {Roo.form.ComboBoxArray.Item} item removed item
19474              */
19475         'remove' : true
19476         
19477         
19478     });
19479     
19480     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19481     
19482     this.items = new Roo.util.MixedCollection(false);
19483     
19484     // construct the child combo...
19485     
19486     
19487     
19488     
19489    
19490     
19491 }
19492
19493  
19494 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19495
19496     /**
19497      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19498      */
19499     
19500     lastData : false,
19501     
19502     // behavies liek a hiddne field
19503     inputType:      'hidden',
19504     /**
19505      * @cfg {Number} width The width of the box that displays the selected element
19506      */ 
19507     width:          300,
19508
19509     
19510     
19511     /**
19512      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19513      */
19514     name : false,
19515     /**
19516      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19517      */
19518     hiddenName : false,
19519       /**
19520      * @cfg {String} seperator    The value seperator normally ',' 
19521      */
19522     seperator : ',',
19523     
19524     
19525         // private the array of items that are displayed..
19526     items  : false,
19527     // private - the hidden field el.
19528     hiddenEl : false,
19529     // private - the filed el..
19530     el : false,
19531     
19532     //validateValue : function() { return true; }, // all values are ok!
19533     //onAddClick: function() { },
19534     
19535     onRender : function(ct, position) 
19536     {
19537         
19538         // create the standard hidden element
19539         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19540         
19541         
19542         // give fake names to child combo;
19543         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19544         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19545         
19546         this.combo = Roo.factory(this.combo, Roo.form);
19547         this.combo.onRender(ct, position);
19548         if (typeof(this.combo.width) != 'undefined') {
19549             this.combo.onResize(this.combo.width,0);
19550         }
19551         
19552         this.combo.initEvents();
19553         
19554         // assigned so form know we need to do this..
19555         this.store          = this.combo.store;
19556         this.valueField     = this.combo.valueField;
19557         this.displayField   = this.combo.displayField ;
19558         
19559         
19560         this.combo.wrap.addClass('x-cbarray-grp');
19561         
19562         var cbwrap = this.combo.wrap.createChild(
19563             {tag: 'div', cls: 'x-cbarray-cb'},
19564             this.combo.el.dom
19565         );
19566         
19567              
19568         this.hiddenEl = this.combo.wrap.createChild({
19569             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19570         });
19571         this.el = this.combo.wrap.createChild({
19572             tag: 'input',  type:'hidden' , name: this.name, value : ''
19573         });
19574          //   this.el.dom.removeAttribute("name");
19575         
19576         
19577         this.outerWrap = this.combo.wrap;
19578         this.wrap = cbwrap;
19579         
19580         this.outerWrap.setWidth(this.width);
19581         this.outerWrap.dom.removeChild(this.el.dom);
19582         
19583         this.wrap.dom.appendChild(this.el.dom);
19584         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19585         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19586         
19587         this.combo.trigger.setStyle('position','relative');
19588         this.combo.trigger.setStyle('left', '0px');
19589         this.combo.trigger.setStyle('top', '2px');
19590         
19591         this.combo.el.setStyle('vertical-align', 'text-bottom');
19592         
19593         //this.trigger.setStyle('vertical-align', 'top');
19594         
19595         // this should use the code from combo really... on('add' ....)
19596         if (this.adder) {
19597             
19598         
19599             this.adder = this.outerWrap.createChild(
19600                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19601             var _t = this;
19602             this.adder.on('click', function(e) {
19603                 _t.fireEvent('adderclick', this, e);
19604             }, _t);
19605         }
19606         //var _t = this;
19607         //this.adder.on('click', this.onAddClick, _t);
19608         
19609         
19610         this.combo.on('select', function(cb, rec, ix) {
19611             this.addItem(rec.data);
19612             
19613             cb.setValue('');
19614             cb.el.dom.value = '';
19615             //cb.lastData = rec.data;
19616             // add to list
19617             
19618         }, this);
19619          
19620         
19621         
19622             
19623     },
19624     
19625     
19626     getName: function()
19627     {
19628         // returns hidden if it's set..
19629         if (!this.rendered) {return ''};
19630         return  this.hiddenName ? this.hiddenName : this.name;
19631         
19632     },
19633     
19634     
19635     onResize: function(w, h){
19636         
19637         return;
19638         // not sure if this is needed..
19639         //this.combo.onResize(w,h);
19640         
19641         if(typeof w != 'number'){
19642             // we do not handle it!?!?
19643             return;
19644         }
19645         var tw = this.combo.trigger.getWidth();
19646         tw += this.addicon ? this.addicon.getWidth() : 0;
19647         tw += this.editicon ? this.editicon.getWidth() : 0;
19648         var x = w - tw;
19649         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19650             
19651         this.combo.trigger.setStyle('left', '0px');
19652         
19653         if(this.list && this.listWidth === undefined){
19654             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19655             this.list.setWidth(lw);
19656             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19657         }
19658         
19659     
19660         
19661     },
19662     
19663     addItem: function(rec)
19664     {
19665         var valueField = this.combo.valueField;
19666         var displayField = this.combo.displayField;
19667         
19668         if (this.items.indexOfKey(rec[valueField]) > -1) {
19669             //console.log("GOT " + rec.data.id);
19670             return;
19671         }
19672         
19673         var x = new Roo.form.ComboBoxArray.Item({
19674             //id : rec[this.idField],
19675             data : rec,
19676             displayField : displayField ,
19677             tipField : displayField ,
19678             cb : this
19679         });
19680         // use the 
19681         this.items.add(rec[valueField],x);
19682         // add it before the element..
19683         this.updateHiddenEl();
19684         x.render(this.outerWrap, this.wrap.dom);
19685         // add the image handler..
19686     },
19687     
19688     updateHiddenEl : function()
19689     {
19690         this.validate();
19691         if (!this.hiddenEl) {
19692             return;
19693         }
19694         var ar = [];
19695         var idField = this.combo.valueField;
19696         
19697         this.items.each(function(f) {
19698             ar.push(f.data[idField]);
19699         });
19700         this.hiddenEl.dom.value = ar.join(this.seperator);
19701         this.validate();
19702     },
19703     
19704     reset : function()
19705     {
19706         this.items.clear();
19707         
19708         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19709            el.remove();
19710         });
19711         
19712         this.el.dom.value = '';
19713         if (this.hiddenEl) {
19714             this.hiddenEl.dom.value = '';
19715         }
19716         
19717     },
19718     getValue: function()
19719     {
19720         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19721     },
19722     setValue: function(v) // not a valid action - must use addItems..
19723     {
19724         
19725         this.reset();
19726          
19727         if (this.store.isLocal && (typeof(v) == 'string')) {
19728             // then we can use the store to find the values..
19729             // comma seperated at present.. this needs to allow JSON based encoding..
19730             this.hiddenEl.value  = v;
19731             var v_ar = [];
19732             Roo.each(v.split(this.seperator), function(k) {
19733                 Roo.log("CHECK " + this.valueField + ',' + k);
19734                 var li = this.store.query(this.valueField, k);
19735                 if (!li.length) {
19736                     return;
19737                 }
19738                 var add = {};
19739                 add[this.valueField] = k;
19740                 add[this.displayField] = li.item(0).data[this.displayField];
19741                 
19742                 this.addItem(add);
19743             }, this) 
19744              
19745         }
19746         if (typeof(v) == 'object' ) {
19747             // then let's assume it's an array of objects..
19748             Roo.each(v, function(l) {
19749                 var add = l;
19750                 if (typeof(l) == 'string') {
19751                     add = {};
19752                     add[this.valueField] = l;
19753                     add[this.displayField] = l
19754                 }
19755                 this.addItem(add);
19756             }, this);
19757              
19758         }
19759         
19760         
19761     },
19762     setFromData: function(v)
19763     {
19764         // this recieves an object, if setValues is called.
19765         this.reset();
19766         this.el.dom.value = v[this.displayField];
19767         this.hiddenEl.dom.value = v[this.valueField];
19768         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19769             return;
19770         }
19771         var kv = v[this.valueField];
19772         var dv = v[this.displayField];
19773         kv = typeof(kv) != 'string' ? '' : kv;
19774         dv = typeof(dv) != 'string' ? '' : dv;
19775         
19776         
19777         var keys = kv.split(this.seperator);
19778         var display = dv.split(this.seperator);
19779         for (var i = 0 ; i < keys.length; i++) {
19780             add = {};
19781             add[this.valueField] = keys[i];
19782             add[this.displayField] = display[i];
19783             this.addItem(add);
19784         }
19785       
19786         
19787     },
19788     
19789     /**
19790      * Validates the combox array value
19791      * @return {Boolean} True if the value is valid, else false
19792      */
19793     validate : function(){
19794         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19795             this.clearInvalid();
19796             return true;
19797         }
19798         return false;
19799     },
19800     
19801     validateValue : function(value){
19802         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19803         
19804     },
19805     
19806     /*@
19807      * overide
19808      * 
19809      */
19810     isDirty : function() {
19811         if(this.disabled) {
19812             return false;
19813         }
19814         
19815         try {
19816             var d = Roo.decode(String(this.originalValue));
19817         } catch (e) {
19818             return String(this.getValue()) !== String(this.originalValue);
19819         }
19820         
19821         var originalValue = [];
19822         
19823         for (var i = 0; i < d.length; i++){
19824             originalValue.push(d[i][this.valueField]);
19825         }
19826         
19827         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19828         
19829     }
19830     
19831 });
19832
19833
19834
19835 /**
19836  * @class Roo.form.ComboBoxArray.Item
19837  * @extends Roo.BoxComponent
19838  * A selected item in the list
19839  *  Fred [x]  Brian [x]  [Pick another |v]
19840  * 
19841  * @constructor
19842  * Create a new item.
19843  * @param {Object} config Configuration options
19844  */
19845  
19846 Roo.form.ComboBoxArray.Item = function(config) {
19847     config.id = Roo.id();
19848     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19849 }
19850
19851 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19852     data : {},
19853     cb: false,
19854     displayField : false,
19855     tipField : false,
19856      
19857     
19858     defaultAutoCreate : {
19859         tag: 'div',
19860         cls: 'x-cbarray-item',
19861         cn : [ 
19862             { tag: 'div' },
19863             {
19864                 tag: 'img',
19865                 width:16,
19866                 height : 16,
19867                 src : Roo.BLANK_IMAGE_URL ,
19868                 align: 'center'
19869             }
19870         ]
19871         
19872     },
19873     
19874  
19875     onRender : function(ct, position)
19876     {
19877         Roo.form.Field.superclass.onRender.call(this, ct, position);
19878         
19879         if(!this.el){
19880             var cfg = this.getAutoCreate();
19881             this.el = ct.createChild(cfg, position);
19882         }
19883         
19884         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19885         
19886         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19887             this.cb.renderer(this.data) :
19888             String.format('{0}',this.data[this.displayField]);
19889         
19890             
19891         this.el.child('div').dom.setAttribute('qtip',
19892                         String.format('{0}',this.data[this.tipField])
19893         );
19894         
19895         this.el.child('img').on('click', this.remove, this);
19896         
19897     },
19898    
19899     remove : function()
19900     {
19901         if(this.cb.disabled){
19902             return;
19903         }
19904         
19905         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19906             this.cb.items.remove(this);
19907             this.el.child('img').un('click', this.remove, this);
19908             this.el.remove();
19909             this.cb.updateHiddenEl();
19910
19911             this.cb.fireEvent('remove', this.cb, this);
19912         }
19913         
19914     }
19915 });/*
19916  * RooJS Library 1.1.1
19917  * Copyright(c) 2008-2011  Alan Knowles
19918  *
19919  * License - LGPL
19920  */
19921  
19922
19923 /**
19924  * @class Roo.form.ComboNested
19925  * @extends Roo.form.ComboBox
19926  * A combobox for that allows selection of nested items in a list,
19927  * eg.
19928  *
19929  *  Book
19930  *    -> red
19931  *    -> green
19932  *  Table
19933  *    -> square
19934  *      ->red
19935  *      ->green
19936  *    -> rectangle
19937  *      ->green
19938  *      
19939  * 
19940  * @constructor
19941  * Create a new ComboNested
19942  * @param {Object} config Configuration options
19943  */
19944 Roo.form.ComboNested = function(config){
19945     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19946     // should verify some data...
19947     // like
19948     // hiddenName = required..
19949     // displayField = required
19950     // valudField == required
19951     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19952     var _t = this;
19953     Roo.each(req, function(e) {
19954         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19955             throw "Roo.form.ComboNested : missing value for: " + e;
19956         }
19957     });
19958      
19959     
19960 };
19961
19962 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19963    
19964     /*
19965      * @config {Number} max Number of columns to show
19966      */
19967     
19968     maxColumns : 3,
19969    
19970     list : null, // the outermost div..
19971     innerLists : null, // the
19972     views : null,
19973     stores : null,
19974     // private
19975     loadingChildren : false,
19976     
19977     onRender : function(ct, position)
19978     {
19979         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19980         
19981         if(this.hiddenName){
19982             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19983                     'before', true);
19984             this.hiddenField.value =
19985                 this.hiddenValue !== undefined ? this.hiddenValue :
19986                 this.value !== undefined ? this.value : '';
19987
19988             // prevent input submission
19989             this.el.dom.removeAttribute('name');
19990              
19991              
19992         }
19993         
19994         if(Roo.isGecko){
19995             this.el.dom.setAttribute('autocomplete', 'off');
19996         }
19997
19998         var cls = 'x-combo-list';
19999
20000         this.list = new Roo.Layer({
20001             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20002         });
20003
20004         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20005         this.list.setWidth(lw);
20006         this.list.swallowEvent('mousewheel');
20007         this.assetHeight = 0;
20008
20009         if(this.title){
20010             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20011             this.assetHeight += this.header.getHeight();
20012         }
20013         this.innerLists = [];
20014         this.views = [];
20015         this.stores = [];
20016         for (var i =0 ; i < this.maxColumns; i++) {
20017             this.onRenderList( cls, i);
20018         }
20019         
20020         // always needs footer, as we are going to have an 'OK' button.
20021         this.footer = this.list.createChild({cls:cls+'-ft'});
20022         this.pageTb = new Roo.Toolbar(this.footer);  
20023         var _this = this;
20024         this.pageTb.add(  {
20025             
20026             text: 'Done',
20027             handler: function()
20028             {
20029                 _this.collapse();
20030             }
20031         });
20032         
20033         if ( this.allowBlank && !this.disableClear) {
20034             
20035             this.pageTb.add(new Roo.Toolbar.Fill(), {
20036                 cls: 'x-btn-icon x-btn-clear',
20037                 text: '&#160;',
20038                 handler: function()
20039                 {
20040                     _this.collapse();
20041                     _this.clearValue();
20042                     _this.onSelect(false, -1);
20043                 }
20044             });
20045         }
20046         if (this.footer) {
20047             this.assetHeight += this.footer.getHeight();
20048         }
20049         
20050     },
20051     onRenderList : function (  cls, i)
20052     {
20053         
20054         var lw = Math.floor(
20055                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20056         );
20057         
20058         this.list.setWidth(lw); // default to '1'
20059
20060         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20061         //il.on('mouseover', this.onViewOver, this, { list:  i });
20062         //il.on('mousemove', this.onViewMove, this, { list:  i });
20063         il.setWidth(lw);
20064         il.setStyle({ 'overflow-x' : 'hidden'});
20065
20066         if(!this.tpl){
20067             this.tpl = new Roo.Template({
20068                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20069                 isEmpty: function (value, allValues) {
20070                     //Roo.log(value);
20071                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20072                     return dl ? 'has-children' : 'no-children'
20073                 }
20074             });
20075         }
20076         
20077         var store  = this.store;
20078         if (i > 0) {
20079             store  = new Roo.data.SimpleStore({
20080                 //fields : this.store.reader.meta.fields,
20081                 reader : this.store.reader,
20082                 data : [ ]
20083             });
20084         }
20085         this.stores[i]  = store;
20086                   
20087         var view = this.views[i] = new Roo.View(
20088             il,
20089             this.tpl,
20090             {
20091                 singleSelect:true,
20092                 store: store,
20093                 selectedClass: this.selectedClass
20094             }
20095         );
20096         view.getEl().setWidth(lw);
20097         view.getEl().setStyle({
20098             position: i < 1 ? 'relative' : 'absolute',
20099             top: 0,
20100             left: (i * lw ) + 'px',
20101             display : i > 0 ? 'none' : 'block'
20102         });
20103         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20104         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20105         //view.on('click', this.onViewClick, this, { list : i });
20106
20107         store.on('beforeload', this.onBeforeLoad, this);
20108         store.on('load',  this.onLoad, this, { list  : i});
20109         store.on('loadexception', this.onLoadException, this);
20110
20111         // hide the other vies..
20112         
20113         
20114         
20115     },
20116       
20117     restrictHeight : function()
20118     {
20119         var mh = 0;
20120         Roo.each(this.innerLists, function(il,i) {
20121             var el = this.views[i].getEl();
20122             el.dom.style.height = '';
20123             var inner = el.dom;
20124             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20125             // only adjust heights on other ones..
20126             mh = Math.max(h, mh);
20127             if (i < 1) {
20128                 
20129                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20130                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20131                
20132             }
20133             
20134             
20135         }, this);
20136         
20137         this.list.beginUpdate();
20138         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20139         this.list.alignTo(this.el, this.listAlign);
20140         this.list.endUpdate();
20141         
20142     },
20143      
20144     
20145     // -- store handlers..
20146     // private
20147     onBeforeLoad : function()
20148     {
20149         if(!this.hasFocus){
20150             return;
20151         }
20152         this.innerLists[0].update(this.loadingText ?
20153                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20154         this.restrictHeight();
20155         this.selectedIndex = -1;
20156     },
20157     // private
20158     onLoad : function(a,b,c,d)
20159     {
20160         if (!this.loadingChildren) {
20161             // then we are loading the top level. - hide the children
20162             for (var i = 1;i < this.views.length; i++) {
20163                 this.views[i].getEl().setStyle({ display : 'none' });
20164             }
20165             var lw = Math.floor(
20166                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20167             );
20168         
20169              this.list.setWidth(lw); // default to '1'
20170
20171             
20172         }
20173         if(!this.hasFocus){
20174             return;
20175         }
20176         
20177         if(this.store.getCount() > 0) {
20178             this.expand();
20179             this.restrictHeight();   
20180         } else {
20181             this.onEmptyResults();
20182         }
20183         
20184         if (!this.loadingChildren) {
20185             this.selectActive();
20186         }
20187         /*
20188         this.stores[1].loadData([]);
20189         this.stores[2].loadData([]);
20190         this.views
20191         */    
20192     
20193         //this.el.focus();
20194     },
20195     
20196     
20197     // private
20198     onLoadException : function()
20199     {
20200         this.collapse();
20201         Roo.log(this.store.reader.jsonData);
20202         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20203             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20204         }
20205         
20206         
20207     },
20208     // no cleaning of leading spaces on blur here.
20209     cleanLeadingSpace : function(e) { },
20210     
20211
20212     onSelectChange : function (view, sels, opts )
20213     {
20214         var ix = view.getSelectedIndexes();
20215          
20216         if (opts.list > this.maxColumns - 2) {
20217             if (view.store.getCount()<  1) {
20218                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20219
20220             } else  {
20221                 if (ix.length) {
20222                     // used to clear ?? but if we are loading unselected 
20223                     this.setFromData(view.store.getAt(ix[0]).data);
20224                 }
20225                 
20226             }
20227             
20228             return;
20229         }
20230         
20231         if (!ix.length) {
20232             // this get's fired when trigger opens..
20233            // this.setFromData({});
20234             var str = this.stores[opts.list+1];
20235             str.data.clear(); // removeall wihtout the fire events..
20236             return;
20237         }
20238         
20239         var rec = view.store.getAt(ix[0]);
20240          
20241         this.setFromData(rec.data);
20242         this.fireEvent('select', this, rec, ix[0]);
20243         
20244         var lw = Math.floor(
20245              (
20246                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20247              ) / this.maxColumns
20248         );
20249         this.loadingChildren = true;
20250         this.stores[opts.list+1].loadDataFromChildren( rec );
20251         this.loadingChildren = false;
20252         var dl = this.stores[opts.list+1]. getTotalCount();
20253         
20254         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20255         
20256         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20257         for (var i = opts.list+2; i < this.views.length;i++) {
20258             this.views[i].getEl().setStyle({ display : 'none' });
20259         }
20260         
20261         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20262         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20263         
20264         if (this.isLoading) {
20265            // this.selectActive(opts.list);
20266         }
20267          
20268     },
20269     
20270     
20271     
20272     
20273     onDoubleClick : function()
20274     {
20275         this.collapse(); //??
20276     },
20277     
20278      
20279     
20280     
20281     
20282     // private
20283     recordToStack : function(store, prop, value, stack)
20284     {
20285         var cstore = new Roo.data.SimpleStore({
20286             //fields : this.store.reader.meta.fields, // we need array reader.. for
20287             reader : this.store.reader,
20288             data : [ ]
20289         });
20290         var _this = this;
20291         var record  = false;
20292         var srec = false;
20293         if(store.getCount() < 1){
20294             return false;
20295         }
20296         store.each(function(r){
20297             if(r.data[prop] == value){
20298                 record = r;
20299             srec = r;
20300                 return false;
20301             }
20302             if (r.data.cn && r.data.cn.length) {
20303                 cstore.loadDataFromChildren( r);
20304                 var cret = _this.recordToStack(cstore, prop, value, stack);
20305                 if (cret !== false) {
20306                     record = cret;
20307                     srec = r;
20308                     return false;
20309                 }
20310             }
20311              
20312             return true;
20313         });
20314         if (record == false) {
20315             return false
20316         }
20317         stack.unshift(srec);
20318         return record;
20319     },
20320     
20321     /*
20322      * find the stack of stores that match our value.
20323      *
20324      * 
20325      */
20326     
20327     selectActive : function ()
20328     {
20329         // if store is not loaded, then we will need to wait for that to happen first.
20330         var stack = [];
20331         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20332         for (var i = 0; i < stack.length; i++ ) {
20333             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20334         }
20335         
20336     }
20337         
20338          
20339     
20340     
20341     
20342     
20343 });/*
20344  * Based on:
20345  * Ext JS Library 1.1.1
20346  * Copyright(c) 2006-2007, Ext JS, LLC.
20347  *
20348  * Originally Released Under LGPL - original licence link has changed is not relivant.
20349  *
20350  * Fork - LGPL
20351  * <script type="text/javascript">
20352  */
20353 /**
20354  * @class Roo.form.Checkbox
20355  * @extends Roo.form.Field
20356  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20357  * @constructor
20358  * Creates a new Checkbox
20359  * @param {Object} config Configuration options
20360  */
20361 Roo.form.Checkbox = function(config){
20362     Roo.form.Checkbox.superclass.constructor.call(this, config);
20363     this.addEvents({
20364         /**
20365          * @event check
20366          * Fires when the checkbox is checked or unchecked.
20367              * @param {Roo.form.Checkbox} this This checkbox
20368              * @param {Boolean} checked The new checked value
20369              */
20370         check : true
20371     });
20372 };
20373
20374 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20375     /**
20376      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20377      */
20378     focusClass : undefined,
20379     /**
20380      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20381      */
20382     fieldClass: "x-form-field",
20383     /**
20384      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20385      */
20386     checked: false,
20387     /**
20388      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20389      * {tag: "input", type: "checkbox", autocomplete: "off"})
20390      */
20391     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20392     /**
20393      * @cfg {String} boxLabel The text that appears beside the checkbox
20394      */
20395     boxLabel : "",
20396     /**
20397      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20398      */  
20399     inputValue : '1',
20400     /**
20401      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20402      */
20403      valueOff: '0', // value when not checked..
20404
20405     actionMode : 'viewEl', 
20406     //
20407     // private
20408     itemCls : 'x-menu-check-item x-form-item',
20409     groupClass : 'x-menu-group-item',
20410     inputType : 'hidden',
20411     
20412     
20413     inSetChecked: false, // check that we are not calling self...
20414     
20415     inputElement: false, // real input element?
20416     basedOn: false, // ????
20417     
20418     isFormField: true, // not sure where this is needed!!!!
20419
20420     onResize : function(){
20421         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20422         if(!this.boxLabel){
20423             this.el.alignTo(this.wrap, 'c-c');
20424         }
20425     },
20426
20427     initEvents : function(){
20428         Roo.form.Checkbox.superclass.initEvents.call(this);
20429         this.el.on("click", this.onClick,  this);
20430         this.el.on("change", this.onClick,  this);
20431     },
20432
20433
20434     getResizeEl : function(){
20435         return this.wrap;
20436     },
20437
20438     getPositionEl : function(){
20439         return this.wrap;
20440     },
20441
20442     // private
20443     onRender : function(ct, position){
20444         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20445         /*
20446         if(this.inputValue !== undefined){
20447             this.el.dom.value = this.inputValue;
20448         }
20449         */
20450         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20451         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20452         var viewEl = this.wrap.createChild({ 
20453             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20454         this.viewEl = viewEl;   
20455         this.wrap.on('click', this.onClick,  this); 
20456         
20457         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20458         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20459         
20460         
20461         
20462         if(this.boxLabel){
20463             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20464         //    viewEl.on('click', this.onClick,  this); 
20465         }
20466         //if(this.checked){
20467             this.setChecked(this.checked);
20468         //}else{
20469             //this.checked = this.el.dom;
20470         //}
20471
20472     },
20473
20474     // private
20475     initValue : Roo.emptyFn,
20476
20477     /**
20478      * Returns the checked state of the checkbox.
20479      * @return {Boolean} True if checked, else false
20480      */
20481     getValue : function(){
20482         if(this.el){
20483             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20484         }
20485         return this.valueOff;
20486         
20487     },
20488
20489         // private
20490     onClick : function(){ 
20491         if (this.disabled) {
20492             return;
20493         }
20494         this.setChecked(!this.checked);
20495
20496         //if(this.el.dom.checked != this.checked){
20497         //    this.setValue(this.el.dom.checked);
20498        // }
20499     },
20500
20501     /**
20502      * Sets the checked state of the checkbox.
20503      * On is always based on a string comparison between inputValue and the param.
20504      * @param {Boolean/String} value - the value to set 
20505      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20506      */
20507     setValue : function(v,suppressEvent){
20508         
20509         
20510         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20511         //if(this.el && this.el.dom){
20512         //    this.el.dom.checked = this.checked;
20513         //    this.el.dom.defaultChecked = this.checked;
20514         //}
20515         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20516         //this.fireEvent("check", this, this.checked);
20517     },
20518     // private..
20519     setChecked : function(state,suppressEvent)
20520     {
20521         if (this.inSetChecked) {
20522             this.checked = state;
20523             return;
20524         }
20525         
20526     
20527         if(this.wrap){
20528             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20529         }
20530         this.checked = state;
20531         if(suppressEvent !== true){
20532             this.fireEvent('check', this, state);
20533         }
20534         this.inSetChecked = true;
20535                  
20536                 this.el.dom.value = state ? this.inputValue : this.valueOff;
20537                  
20538         this.inSetChecked = false;
20539         
20540     },
20541     // handle setting of hidden value by some other method!!?!?
20542     setFromHidden: function()
20543     {
20544         if(!this.el){
20545             return;
20546         }
20547         //console.log("SET FROM HIDDEN");
20548         //alert('setFrom hidden');
20549         this.setValue(this.el.dom.value);
20550     },
20551     
20552     onDestroy : function()
20553     {
20554         if(this.viewEl){
20555             Roo.get(this.viewEl).remove();
20556         }
20557          
20558         Roo.form.Checkbox.superclass.onDestroy.call(this);
20559     },
20560     
20561     setBoxLabel : function(str)
20562     {
20563         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20564     }
20565
20566 });/*
20567  * Based on:
20568  * Ext JS Library 1.1.1
20569  * Copyright(c) 2006-2007, Ext JS, LLC.
20570  *
20571  * Originally Released Under LGPL - original licence link has changed is not relivant.
20572  *
20573  * Fork - LGPL
20574  * <script type="text/javascript">
20575  */
20576  
20577 /**
20578  * @class Roo.form.Radio
20579  * @extends Roo.form.Checkbox
20580  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20581  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20582  * @constructor
20583  * Creates a new Radio
20584  * @param {Object} config Configuration options
20585  */
20586 Roo.form.Radio = function(){
20587     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20588 };
20589 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20590     inputType: 'radio',
20591
20592     /**
20593      * If this radio is part of a group, it will return the selected value
20594      * @return {String}
20595      */
20596     getGroupValue : function(){
20597         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20598     },
20599     
20600     
20601     onRender : function(ct, position){
20602         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20603         
20604         if(this.inputValue !== undefined){
20605             this.el.dom.value = this.inputValue;
20606         }
20607          
20608         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20609         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20610         //var viewEl = this.wrap.createChild({ 
20611         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20612         //this.viewEl = viewEl;   
20613         //this.wrap.on('click', this.onClick,  this); 
20614         
20615         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20616         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20617         
20618         
20619         
20620         if(this.boxLabel){
20621             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20622         //    viewEl.on('click', this.onClick,  this); 
20623         }
20624          if(this.checked){
20625             this.el.dom.checked =   'checked' ;
20626         }
20627          
20628     },
20629     /**
20630      * Sets the checked state of the checkbox.
20631      * On is always based on a string comparison between inputValue and the param.
20632      * @param {Boolean/String} value - the value to set 
20633      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20634      */
20635     setValue : function(v,suppressEvent){
20636         
20637         
20638         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20639         //if(this.el && this.el.dom){
20640         //    this.el.dom.checked = this.checked;
20641         //    this.el.dom.defaultChecked = this.checked;
20642         //}
20643         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20644         
20645         this.el.dom.form[this.name].value = v;
20646      
20647         //this.fireEvent("check", this, this.checked);
20648     },
20649     // private..
20650     setChecked : function(state,suppressEvent)
20651     {
20652          
20653         if(this.wrap){
20654             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20655         }
20656         this.checked = state;
20657         if(suppressEvent !== true){
20658             this.fireEvent('check', this, state);
20659         }
20660                  
20661                   
20662        
20663         
20664     },
20665     reset : function(){
20666         // this.setValue(this.resetValue);
20667         //this.originalValue = this.getValue();
20668         this.clearInvalid();
20669     } 
20670     
20671 });Roo.rtf = {}; // namespace
20672 Roo.rtf.Hex = function(hex)
20673 {
20674     this.hexstr = hex;
20675 };
20676 Roo.rtf.Paragraph = function(opts)
20677 {
20678     this.content = []; ///??? is that used?
20679 };Roo.rtf.Span = function(opts)
20680 {
20681     this.value = opts.value;
20682 };
20683
20684 Roo.rtf.Group = function(parent)
20685 {
20686     // we dont want to acutally store parent - it will make debug a nightmare..
20687     this.content = [];
20688     this.cn  = [];
20689      
20690        
20691     
20692 };
20693
20694 Roo.rtf.Group.prototype = {
20695     ignorable : false,
20696     content: false,
20697     cn: false,
20698     addContent : function(node) {
20699         // could set styles...
20700         this.content.push(node);
20701     },
20702     addChild : function(cn)
20703     {
20704         this.cn.push(cn);
20705     },
20706     // only for images really...
20707     toDataURL : function()
20708     {
20709         var mimetype = false;
20710         switch(true) {
20711             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20712                 mimetype = "image/png";
20713                 break;
20714              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20715                 mimetype = "image/jpeg";
20716                 break;
20717             default :
20718                 return 'about:blank'; // ?? error?
20719         }
20720         
20721         
20722         var hexstring = this.content[this.content.length-1].value;
20723         
20724         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20725             return String.fromCharCode(parseInt(a, 16));
20726         }).join(""));
20727     }
20728     
20729 };
20730 // this looks like it's normally the {rtf{ .... }}
20731 Roo.rtf.Document = function()
20732 {
20733     // we dont want to acutally store parent - it will make debug a nightmare..
20734     this.rtlch  = [];
20735     this.content = [];
20736     this.cn = [];
20737     
20738 };
20739 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20740     addChild : function(cn)
20741     {
20742         this.cn.push(cn);
20743         switch(cn.type) {
20744             case 'rtlch': // most content seems to be inside this??
20745             case 'listtext':
20746             case 'shpinst':
20747                 this.rtlch.push(cn);
20748                 return;
20749             default:
20750                 this[cn.type] = cn;
20751         }
20752         
20753     },
20754     
20755     getElementsByType : function(type)
20756     {
20757         var ret =  [];
20758         this._getElementsByType(type, ret, this.cn, 'rtf');
20759         return ret;
20760     },
20761     _getElementsByType : function (type, ret, search_array, path)
20762     {
20763         search_array.forEach(function(n,i) {
20764             if (n.type == type) {
20765                 n.path = path + '/' + n.type + ':' + i;
20766                 ret.push(n);
20767             }
20768             if (n.cn.length > 0) {
20769                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20770             }
20771         },this);
20772     }
20773     
20774 });
20775  
20776 Roo.rtf.Ctrl = function(opts)
20777 {
20778     this.value = opts.value;
20779     this.param = opts.param;
20780 };
20781 /**
20782  *
20783  *
20784  * based on this https://github.com/iarna/rtf-parser
20785  * it's really only designed to extract pict from pasted RTF 
20786  *
20787  * usage:
20788  *
20789  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20790  *  
20791  *
20792  */
20793
20794  
20795
20796
20797
20798 Roo.rtf.Parser = function(text) {
20799     //super({objectMode: true})
20800     this.text = '';
20801     this.parserState = this.parseText;
20802     
20803     // these are for interpeter...
20804     this.doc = {};
20805     ///this.parserState = this.parseTop
20806     this.groupStack = [];
20807     this.hexStore = [];
20808     this.doc = false;
20809     
20810     this.groups = []; // where we put the return.
20811     
20812     for (var ii = 0; ii < text.length; ++ii) {
20813         ++this.cpos;
20814         
20815         if (text[ii] === '\n') {
20816             ++this.row;
20817             this.col = 1;
20818         } else {
20819             ++this.col;
20820         }
20821         this.parserState(text[ii]);
20822     }
20823     
20824     
20825     
20826 };
20827 Roo.rtf.Parser.prototype = {
20828     text : '', // string being parsed..
20829     controlWord : '',
20830     controlWordParam :  '',
20831     hexChar : '',
20832     doc : false,
20833     group: false,
20834     groupStack : false,
20835     hexStore : false,
20836     
20837     
20838     cpos : 0, 
20839     row : 1, // reportin?
20840     col : 1, //
20841
20842      
20843     push : function (el)
20844     {
20845         var m = 'cmd'+ el.type;
20846         if (typeof(this[m]) == 'undefined') {
20847             Roo.log('invalid cmd:' + el.type);
20848             return;
20849         }
20850         this[m](el);
20851         //Roo.log(el);
20852     },
20853     flushHexStore : function()
20854     {
20855         if (this.hexStore.length < 1) {
20856             return;
20857         }
20858         var hexstr = this.hexStore.map(
20859             function(cmd) {
20860                 return cmd.value;
20861         }).join('');
20862         
20863         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20864               
20865             
20866         this.hexStore.splice(0)
20867         
20868     },
20869     
20870     cmdgroupstart : function()
20871     {
20872         this.flushHexStore();
20873         if (this.group) {
20874             this.groupStack.push(this.group);
20875         }
20876          // parent..
20877         if (this.doc === false) {
20878             this.group = this.doc = new Roo.rtf.Document();
20879             return;
20880             
20881         }
20882         this.group = new Roo.rtf.Group(this.group);
20883     },
20884     cmdignorable : function()
20885     {
20886         this.flushHexStore();
20887         this.group.ignorable = true;
20888     },
20889     cmdendparagraph : function()
20890     {
20891         this.flushHexStore();
20892         this.group.addContent(new Roo.rtf.Paragraph());
20893     },
20894     cmdgroupend : function ()
20895     {
20896         this.flushHexStore();
20897         var endingGroup = this.group;
20898         
20899         
20900         this.group = this.groupStack.pop();
20901         if (this.group) {
20902             this.group.addChild(endingGroup);
20903         }
20904         
20905         
20906         
20907         var doc = this.group || this.doc;
20908         //if (endingGroup instanceof FontTable) {
20909         //  doc.fonts = endingGroup.table
20910         //} else if (endingGroup instanceof ColorTable) {
20911         //  doc.colors = endingGroup.table
20912         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20913         if (endingGroup.ignorable === false) {
20914             //code
20915             this.groups.push(endingGroup);
20916            // Roo.log( endingGroup );
20917         }
20918             //Roo.each(endingGroup.content, function(item)) {
20919             //    doc.addContent(item);
20920             //}
20921             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20922         //}
20923     },
20924     cmdtext : function (cmd)
20925     {
20926         this.flushHexStore();
20927         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20928             //this.group = this.doc
20929             return;  // we really don't care about stray text...
20930         }
20931         this.group.addContent(new Roo.rtf.Span(cmd));
20932     },
20933     cmdcontrolword : function (cmd)
20934     {
20935         this.flushHexStore();
20936         if (!this.group.type) {
20937             this.group.type = cmd.value;
20938             return;
20939         }
20940         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20941         // we actually don't care about ctrl words...
20942         return ;
20943         /*
20944         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20945         if (this[method]) {
20946             this[method](cmd.param)
20947         } else {
20948             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20949         }
20950         */
20951     },
20952     cmdhexchar : function(cmd) {
20953         this.hexStore.push(cmd);
20954     },
20955     cmderror : function(cmd) {
20956         throw cmd.value;
20957     },
20958     
20959     /*
20960       _flush (done) {
20961         if (this.text !== '\u0000') this.emitText()
20962         done()
20963       }
20964       */
20965       
20966       
20967     parseText : function(c)
20968     {
20969         if (c === '\\') {
20970             this.parserState = this.parseEscapes;
20971         } else if (c === '{') {
20972             this.emitStartGroup();
20973         } else if (c === '}') {
20974             this.emitEndGroup();
20975         } else if (c === '\x0A' || c === '\x0D') {
20976             // cr/lf are noise chars
20977         } else {
20978             this.text += c;
20979         }
20980     },
20981     
20982     parseEscapes: function (c)
20983     {
20984         if (c === '\\' || c === '{' || c === '}') {
20985             this.text += c;
20986             this.parserState = this.parseText;
20987         } else {
20988             this.parserState = this.parseControlSymbol;
20989             this.parseControlSymbol(c);
20990         }
20991     },
20992     parseControlSymbol: function(c)
20993     {
20994         if (c === '~') {
20995             this.text += '\u00a0'; // nbsp
20996             this.parserState = this.parseText
20997         } else if (c === '-') {
20998              this.text += '\u00ad'; // soft hyphen
20999         } else if (c === '_') {
21000             this.text += '\u2011'; // non-breaking hyphen
21001         } else if (c === '*') {
21002             this.emitIgnorable();
21003             this.parserState = this.parseText;
21004         } else if (c === "'") {
21005             this.parserState = this.parseHexChar;
21006         } else if (c === '|') { // formula cacter
21007             this.emitFormula();
21008             this.parserState = this.parseText;
21009         } else if (c === ':') { // subentry in an index entry
21010             this.emitIndexSubEntry();
21011             this.parserState = this.parseText;
21012         } else if (c === '\x0a') {
21013             this.emitEndParagraph();
21014             this.parserState = this.parseText;
21015         } else if (c === '\x0d') {
21016             this.emitEndParagraph();
21017             this.parserState = this.parseText;
21018         } else {
21019             this.parserState = this.parseControlWord;
21020             this.parseControlWord(c);
21021         }
21022     },
21023     parseHexChar: function (c)
21024     {
21025         if (/^[A-Fa-f0-9]$/.test(c)) {
21026             this.hexChar += c;
21027             if (this.hexChar.length >= 2) {
21028               this.emitHexChar();
21029               this.parserState = this.parseText;
21030             }
21031             return;
21032         }
21033         this.emitError("Invalid character \"" + c + "\" in hex literal.");
21034         this.parserState = this.parseText;
21035         
21036     },
21037     parseControlWord : function(c)
21038     {
21039         if (c === ' ') {
21040             this.emitControlWord();
21041             this.parserState = this.parseText;
21042         } else if (/^[-\d]$/.test(c)) {
21043             this.parserState = this.parseControlWordParam;
21044             this.controlWordParam += c;
21045         } else if (/^[A-Za-z]$/.test(c)) {
21046           this.controlWord += c;
21047         } else {
21048           this.emitControlWord();
21049           this.parserState = this.parseText;
21050           this.parseText(c);
21051         }
21052     },
21053     parseControlWordParam : function (c) {
21054         if (/^\d$/.test(c)) {
21055           this.controlWordParam += c;
21056         } else if (c === ' ') {
21057           this.emitControlWord();
21058           this.parserState = this.parseText;
21059         } else {
21060           this.emitControlWord();
21061           this.parserState = this.parseText;
21062           this.parseText(c);
21063         }
21064     },
21065     
21066     
21067     
21068     
21069     emitText : function () {
21070         if (this.text === '') {
21071             return;
21072         }
21073         this.push({
21074             type: 'text',
21075             value: this.text,
21076             pos: this.cpos,
21077             row: this.row,
21078             col: this.col
21079         });
21080         this.text = ''
21081     },
21082     emitControlWord : function ()
21083     {
21084         this.emitText();
21085         if (this.controlWord === '') {
21086             // do we want to track this - it seems just to cause problems.
21087             //this.emitError('empty control word');
21088         } else {
21089             this.push({
21090                   type: 'controlword',
21091                   value: this.controlWord,
21092                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
21093                   pos: this.cpos,
21094                   row: this.row,
21095                   col: this.col
21096             });
21097         }
21098         this.controlWord = '';
21099         this.controlWordParam = '';
21100     },
21101     emitStartGroup : function ()
21102     {
21103         this.emitText();
21104         this.push({
21105             type: 'groupstart',
21106             pos: this.cpos,
21107             row: this.row,
21108             col: this.col
21109         });
21110     },
21111     emitEndGroup : function ()
21112     {
21113         this.emitText();
21114         this.push({
21115             type: 'groupend',
21116             pos: this.cpos,
21117             row: this.row,
21118             col: this.col
21119         });
21120     },
21121     emitIgnorable : function ()
21122     {
21123         this.emitText();
21124         this.push({
21125             type: 'ignorable',
21126             pos: this.cpos,
21127             row: this.row,
21128             col: this.col
21129         });
21130     },
21131     emitHexChar : function ()
21132     {
21133         this.emitText();
21134         this.push({
21135             type: 'hexchar',
21136             value: this.hexChar,
21137             pos: this.cpos,
21138             row: this.row,
21139             col: this.col
21140         });
21141         this.hexChar = ''
21142     },
21143     emitError : function (message)
21144     {
21145       this.emitText();
21146       this.push({
21147             type: 'error',
21148             value: message,
21149             row: this.row,
21150             col: this.col,
21151             char: this.cpos //,
21152             //stack: new Error().stack
21153         });
21154     },
21155     emitEndParagraph : function () {
21156         this.emitText();
21157         this.push({
21158             type: 'endparagraph',
21159             pos: this.cpos,
21160             row: this.row,
21161             col: this.col
21162         });
21163     }
21164      
21165 } ;
21166 Roo.htmleditor = {};
21167  
21168 /**
21169  * @class Roo.htmleditor.Filter
21170  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21171  * @cfg {DomElement} node The node to iterate and filter
21172  * @cfg {boolean|String|Array} tag Tags to replace 
21173  * @constructor
21174  * Create a new Filter.
21175  * @param {Object} config Configuration options
21176  */
21177
21178
21179
21180 Roo.htmleditor.Filter = function(cfg) {
21181     Roo.apply(this.cfg);
21182     // this does not actually call walk as it's really just a abstract class
21183 }
21184
21185
21186 Roo.htmleditor.Filter.prototype = {
21187     
21188     node: false,
21189     
21190     tag: false,
21191
21192     // overrride to do replace comments.
21193     replaceComment : false,
21194     
21195     // overrride to do replace or do stuff with tags..
21196     replaceTag : false,
21197     
21198     walk : function(dom)
21199     {
21200         Roo.each( Array.from(dom.childNodes), function( e ) {
21201             switch(true) {
21202                 
21203                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
21204                     this.replaceComment(e);
21205                     return;
21206                 
21207                 case e.nodeType != 1: //not a node.
21208                     return;
21209                 
21210                 case this.tag === true: // everything
21211                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
21212                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
21213                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21214                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21215                     if (this.replaceTag && false === this.replaceTag(e)) {
21216                         return;
21217                     }
21218                     if (e.hasChildNodes()) {
21219                         this.walk(e);
21220                     }
21221                     return;
21222                 
21223                 default:    // tags .. that do not match.
21224                     if (e.hasChildNodes()) {
21225                         this.walk(e);
21226                     }
21227             }
21228             
21229         }, this);
21230         
21231     },
21232     
21233     
21234     removeNodeKeepChildren : function( node)
21235     {
21236     
21237         ar = Array.from(node.childNodes);
21238         for (var i = 0; i < ar.length; i++) {
21239          
21240             node.removeChild(ar[i]);
21241             // what if we need to walk these???
21242             node.parentNode.insertBefore(ar[i], node);
21243            
21244         }
21245         node.parentNode.removeChild(node);
21246     }
21247 }; 
21248
21249 /**
21250  * @class Roo.htmleditor.FilterAttributes
21251  * clean attributes and  styles including http:// etc.. in attribute
21252  * @constructor
21253 * Run a new Attribute Filter
21254 * @param {Object} config Configuration options
21255  */
21256 Roo.htmleditor.FilterAttributes = function(cfg)
21257 {
21258     Roo.apply(this, cfg);
21259     this.attrib_black = this.attrib_black || [];
21260     this.attrib_white = this.attrib_white || [];
21261
21262     this.attrib_clean = this.attrib_clean || [];
21263     this.style_white = this.style_white || [];
21264     this.style_black = this.style_black || [];
21265     this.walk(cfg.node);
21266 }
21267
21268 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21269 {
21270     tag: true, // all tags
21271     
21272     attrib_black : false, // array
21273     attrib_clean : false,
21274     attrib_white : false,
21275
21276     style_white : false,
21277     style_black : false,
21278      
21279      
21280     replaceTag : function(node)
21281     {
21282         if (!node.attributes || !node.attributes.length) {
21283             return true;
21284         }
21285         
21286         for (var i = node.attributes.length-1; i > -1 ; i--) {
21287             var a = node.attributes[i];
21288             //console.log(a);
21289             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21290                 node.removeAttribute(a.name);
21291                 continue;
21292             }
21293             
21294             
21295             
21296             if (a.name.toLowerCase().substr(0,2)=='on')  {
21297                 node.removeAttribute(a.name);
21298                 continue;
21299             }
21300             
21301             
21302             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21303                 node.removeAttribute(a.name);
21304                 continue;
21305             }
21306             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21307                 this.cleanAttr(node,a.name,a.value); // fixme..
21308                 continue;
21309             }
21310             if (a.name == 'style') {
21311                 this.cleanStyle(node,a.name,a.value);
21312                 continue;
21313             }
21314             /// clean up MS crap..
21315             // tecnically this should be a list of valid class'es..
21316             
21317             
21318             if (a.name == 'class') {
21319                 if (a.value.match(/^Mso/)) {
21320                     node.removeAttribute('class');
21321                 }
21322                 
21323                 if (a.value.match(/^body$/)) {
21324                     node.removeAttribute('class');
21325                 }
21326                 continue;
21327             }
21328             
21329             
21330             // style cleanup!?
21331             // class cleanup?
21332             
21333         }
21334         return true; // clean children
21335     },
21336         
21337     cleanAttr: function(node, n,v)
21338     {
21339         
21340         if (v.match(/^\./) || v.match(/^\//)) {
21341             return;
21342         }
21343         if (v.match(/^(http|https):\/\//)
21344             || v.match(/^mailto:/) 
21345             || v.match(/^ftp:/)
21346             || v.match(/^data:/)
21347             ) {
21348             return;
21349         }
21350         if (v.match(/^#/)) {
21351             return;
21352         }
21353         if (v.match(/^\{/)) { // allow template editing.
21354             return;
21355         }
21356 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21357         node.removeAttribute(n);
21358         
21359     },
21360     cleanStyle : function(node,  n,v)
21361     {
21362         if (v.match(/expression/)) { //XSS?? should we even bother..
21363             node.removeAttribute(n);
21364             return;
21365         }
21366         
21367         var parts = v.split(/;/);
21368         var clean = [];
21369         
21370         Roo.each(parts, function(p) {
21371             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21372             if (!p.length) {
21373                 return true;
21374             }
21375             var l = p.split(':').shift().replace(/\s+/g,'');
21376             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21377             
21378             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21379                 return true;
21380             }
21381             //Roo.log()
21382             // only allow 'c whitelisted system attributes'
21383             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21384                 return true;
21385             }
21386             
21387             
21388             clean.push(p);
21389             return true;
21390         },this);
21391         if (clean.length) { 
21392             node.setAttribute(n, clean.join(';'));
21393         } else {
21394             node.removeAttribute(n);
21395         }
21396         
21397     }
21398         
21399         
21400         
21401     
21402 });/**
21403  * @class Roo.htmleditor.FilterBlack
21404  * remove blacklisted elements.
21405  * @constructor
21406  * Run a new Blacklisted Filter
21407  * @param {Object} config Configuration options
21408  */
21409
21410 Roo.htmleditor.FilterBlack = function(cfg)
21411 {
21412     Roo.apply(this, cfg);
21413     this.walk(cfg.node);
21414 }
21415
21416 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21417 {
21418     tag : true, // all elements.
21419    
21420     replaceTag : function(n)
21421     {
21422         n.parentNode.removeChild(n);
21423     }
21424 });
21425 /**
21426  * @class Roo.htmleditor.FilterComment
21427  * remove comments.
21428  * @constructor
21429 * Run a new Comments Filter
21430 * @param {Object} config Configuration options
21431  */
21432 Roo.htmleditor.FilterComment = function(cfg)
21433 {
21434     this.walk(cfg.node);
21435 }
21436
21437 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21438 {
21439   
21440     replaceComment : function(n)
21441     {
21442         n.parentNode.removeChild(n);
21443     }
21444 });/**
21445  * @class Roo.htmleditor.FilterKeepChildren
21446  * remove tags but keep children
21447  * @constructor
21448  * Run a new Keep Children Filter
21449  * @param {Object} config Configuration options
21450  */
21451
21452 Roo.htmleditor.FilterKeepChildren = function(cfg)
21453 {
21454     Roo.apply(this, cfg);
21455     if (this.tag === false) {
21456         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21457     }
21458     // hacky?
21459     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
21460         this.cleanNamespace = true;
21461     }
21462         
21463     this.walk(cfg.node);
21464 }
21465
21466 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21467 {
21468     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
21469   
21470     replaceTag : function(node)
21471     {
21472         // walk children...
21473         //Roo.log(node.tagName);
21474         var ar = Array.from(node.childNodes);
21475         //remove first..
21476         
21477         for (var i = 0; i < ar.length; i++) {
21478             var e = ar[i];
21479             if (e.nodeType == 1) {
21480                 if (
21481                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
21482                     || // array and it matches
21483                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
21484                     ||
21485                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
21486                     ||
21487                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
21488                 ) {
21489                     this.replaceTag(ar[i]); // child is blacklisted as well...
21490                     continue;
21491                 }
21492             }
21493         }  
21494         ar = Array.from(node.childNodes);
21495         for (var i = 0; i < ar.length; i++) {
21496          
21497             node.removeChild(ar[i]);
21498             // what if we need to walk these???
21499             node.parentNode.insertBefore(ar[i], node);
21500             if (this.tag !== false) {
21501                 this.walk(ar[i]);
21502                 
21503             }
21504         }
21505         //Roo.log("REMOVE:" + node.tagName);
21506         node.parentNode.removeChild(node);
21507         return false; // don't walk children
21508         
21509         
21510     }
21511 });/**
21512  * @class Roo.htmleditor.FilterParagraph
21513  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21514  * like on 'push' to remove the <p> tags and replace them with line breaks.
21515  * @constructor
21516  * Run a new Paragraph Filter
21517  * @param {Object} config Configuration options
21518  */
21519
21520 Roo.htmleditor.FilterParagraph = function(cfg)
21521 {
21522     // no need to apply config.
21523     this.walk(cfg.node);
21524 }
21525
21526 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21527 {
21528     
21529      
21530     tag : 'P',
21531     
21532      
21533     replaceTag : function(node)
21534     {
21535         
21536         if (node.childNodes.length == 1 &&
21537             node.childNodes[0].nodeType == 3 &&
21538             node.childNodes[0].textContent.trim().length < 1
21539             ) {
21540             // remove and replace with '<BR>';
21541             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21542             return false; // no need to walk..
21543         }
21544         var ar = Array.from(node.childNodes);
21545         for (var i = 0; i < ar.length; i++) {
21546             node.removeChild(ar[i]);
21547             // what if we need to walk these???
21548             node.parentNode.insertBefore(ar[i], node);
21549         }
21550         // now what about this?
21551         // <p> &nbsp; </p>
21552         
21553         // double BR.
21554         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21555         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21556         node.parentNode.removeChild(node);
21557         
21558         return false;
21559
21560     }
21561     
21562 });/**
21563  * @class Roo.htmleditor.FilterSpan
21564  * filter span's with no attributes out..
21565  * @constructor
21566  * Run a new Span Filter
21567  * @param {Object} config Configuration options
21568  */
21569
21570 Roo.htmleditor.FilterSpan = function(cfg)
21571 {
21572     // no need to apply config.
21573     this.walk(cfg.node);
21574 }
21575
21576 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21577 {
21578      
21579     tag : 'SPAN',
21580      
21581  
21582     replaceTag : function(node)
21583     {
21584         if (node.attributes && node.attributes.length > 0) {
21585             return true; // walk if there are any.
21586         }
21587         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21588         return false;
21589      
21590     }
21591     
21592 });/**
21593  * @class Roo.htmleditor.FilterTableWidth
21594   try and remove table width data - as that frequently messes up other stuff.
21595  * 
21596  *      was cleanTableWidths.
21597  *
21598  * Quite often pasting from word etc.. results in tables with column and widths.
21599  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21600  *
21601  * @constructor
21602  * Run a new Table Filter
21603  * @param {Object} config Configuration options
21604  */
21605
21606 Roo.htmleditor.FilterTableWidth = function(cfg)
21607 {
21608     // no need to apply config.
21609     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21610     this.walk(cfg.node);
21611 }
21612
21613 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21614 {
21615      
21616      
21617     
21618     replaceTag: function(node) {
21619         
21620         
21621       
21622         if (node.hasAttribute('width')) {
21623             node.removeAttribute('width');
21624         }
21625         
21626          
21627         if (node.hasAttribute("style")) {
21628             // pretty basic...
21629             
21630             var styles = node.getAttribute("style").split(";");
21631             var nstyle = [];
21632             Roo.each(styles, function(s) {
21633                 if (!s.match(/:/)) {
21634                     return;
21635                 }
21636                 var kv = s.split(":");
21637                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21638                     return;
21639                 }
21640                 // what ever is left... we allow.
21641                 nstyle.push(s);
21642             });
21643             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21644             if (!nstyle.length) {
21645                 node.removeAttribute('style');
21646             }
21647         }
21648         
21649         return true; // continue doing children..
21650     }
21651 });/**
21652  * @class Roo.htmleditor.FilterWord
21653  * try and clean up all the mess that Word generates.
21654  * 
21655  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21656  
21657  * @constructor
21658  * Run a new Span Filter
21659  * @param {Object} config Configuration options
21660  */
21661
21662 Roo.htmleditor.FilterWord = function(cfg)
21663 {
21664     // no need to apply config.
21665     this.replaceDocBullets(cfg.node);
21666     
21667     this.replaceAname(cfg.node);
21668     // this is disabled as the removal is done by other filters;
21669    // this.walk(cfg.node);
21670     this.replaceImageTable(cfg.node);
21671     
21672 }
21673
21674 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21675 {
21676     tag: true,
21677      
21678     
21679     /**
21680      * Clean up MS wordisms...
21681      */
21682     replaceTag : function(node)
21683     {
21684          
21685         // no idea what this does - span with text, replaceds with just text.
21686         if(
21687                 node.nodeName == 'SPAN' &&
21688                 !node.hasAttributes() &&
21689                 node.childNodes.length == 1 &&
21690                 node.firstChild.nodeName == "#text"  
21691         ) {
21692             var textNode = node.firstChild;
21693             node.removeChild(textNode);
21694             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21695                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21696             }
21697             node.parentNode.insertBefore(textNode, node);
21698             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21699                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21700             }
21701             
21702             node.parentNode.removeChild(node);
21703             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21704         }
21705         
21706    
21707         
21708         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21709             node.parentNode.removeChild(node);
21710             return false; // dont do chidlren
21711         }
21712         //Roo.log(node.tagName);
21713         // remove - but keep children..
21714         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21715             //Roo.log('-- removed');
21716             while (node.childNodes.length) {
21717                 var cn = node.childNodes[0];
21718                 node.removeChild(cn);
21719                 node.parentNode.insertBefore(cn, node);
21720                 // move node to parent - and clean it..
21721                 if (cn.nodeType == 1) {
21722                     this.replaceTag(cn);
21723                 }
21724                 
21725             }
21726             node.parentNode.removeChild(node);
21727             /// no need to iterate chidlren = it's got none..
21728             //this.iterateChildren(node, this.cleanWord);
21729             return false; // no need to iterate children.
21730         }
21731         // clean styles
21732         if (node.className.length) {
21733             
21734             var cn = node.className.split(/\W+/);
21735             var cna = [];
21736             Roo.each(cn, function(cls) {
21737                 if (cls.match(/Mso[a-zA-Z]+/)) {
21738                     return;
21739                 }
21740                 cna.push(cls);
21741             });
21742             node.className = cna.length ? cna.join(' ') : '';
21743             if (!cna.length) {
21744                 node.removeAttribute("class");
21745             }
21746         }
21747         
21748         if (node.hasAttribute("lang")) {
21749             node.removeAttribute("lang");
21750         }
21751         
21752         if (node.hasAttribute("style")) {
21753             
21754             var styles = node.getAttribute("style").split(";");
21755             var nstyle = [];
21756             Roo.each(styles, function(s) {
21757                 if (!s.match(/:/)) {
21758                     return;
21759                 }
21760                 var kv = s.split(":");
21761                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21762                     return;
21763                 }
21764                 // what ever is left... we allow.
21765                 nstyle.push(s);
21766             });
21767             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21768             if (!nstyle.length) {
21769                 node.removeAttribute('style');
21770             }
21771         }
21772         return true; // do children
21773         
21774         
21775         
21776     },
21777     
21778     styleToObject: function(node)
21779     {
21780         var styles = (node.getAttribute("style") || '').split(";");
21781         var ret = {};
21782         Roo.each(styles, function(s) {
21783             if (!s.match(/:/)) {
21784                 return;
21785             }
21786             var kv = s.split(":");
21787              
21788             // what ever is left... we allow.
21789             ret[kv[0].trim()] = kv[1];
21790         });
21791         return ret;
21792     },
21793     
21794     
21795     replaceAname : function (doc)
21796     {
21797         // replace all the a/name without..
21798         var aa = Array.from(doc.getElementsByTagName('a'));
21799         for (var i = 0; i  < aa.length; i++) {
21800             var a = aa[i];
21801             if (a.hasAttribute("name")) {
21802                 a.removeAttribute("name");
21803             }
21804             if (a.hasAttribute("href")) {
21805                 continue;
21806             }
21807             // reparent children.
21808             this.removeNodeKeepChildren(a);
21809             
21810         }
21811         
21812         
21813         
21814     },
21815
21816     
21817     
21818     replaceDocBullets : function(doc)
21819     {
21820         // this is a bit odd - but it appears some indents use ql-indent-1
21821          //Roo.log(doc.innerHTML);
21822         
21823         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
21824         for( var i = 0; i < listpara.length; i ++) {
21825             listpara[i].className = "MsoListParagraph";
21826         }
21827         
21828         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
21829         for( var i = 0; i < listpara.length; i ++) {
21830             listpara[i].className = "MsoListParagraph";
21831         }
21832         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
21833         for( var i = 0; i < listpara.length; i ++) {
21834             listpara[i].className = "MsoListParagraph";
21835         }
21836         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
21837         for( var i = 0; i < listpara.length; i ++) {
21838             listpara[i].className = "MsoListParagraph";
21839         }
21840         
21841         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
21842         var htwo =  Array.from(doc.getElementsByTagName('h2'));
21843         for( var i = 0; i < htwo.length; i ++) {
21844             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
21845                 htwo[i].className = "MsoListParagraph";
21846             }
21847         }
21848         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
21849         for( var i = 0; i < listpara.length; i ++) {
21850             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
21851                 listpara[i].className = "MsoListParagraph";
21852             } else {
21853                 listpara[i].className = "MsoNormalx";
21854             }
21855         }
21856        
21857         listpara = doc.getElementsByClassName('MsoListParagraph');
21858         // Roo.log(doc.innerHTML);
21859         
21860         
21861         
21862         while(listpara.length) {
21863             
21864             this.replaceDocBullet(listpara.item(0));
21865         }
21866       
21867     },
21868     
21869      
21870     
21871     replaceDocBullet : function(p)
21872     {
21873         // gather all the siblings.
21874         var ns = p,
21875             parent = p.parentNode,
21876             doc = parent.ownerDocument,
21877             items = [];
21878          
21879         //Roo.log("Parsing: " + p.innerText)    ;
21880         var listtype = 'ul';   
21881         while (ns) {
21882             if (ns.nodeType != 1) {
21883                 ns = ns.nextSibling;
21884                 continue;
21885             }
21886             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
21887                 //Roo.log("Missing para r q1indent - got:" + ns.className);
21888                 break;
21889             }
21890             var spans = ns.getElementsByTagName('span');
21891             
21892             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
21893                 items.push(ns);
21894                 ns = ns.nextSibling;
21895                 has_list = true;
21896                 if (!spans.length) {
21897                     continue;
21898                 }
21899                 var ff = '';
21900                 var se = spans[0];
21901                 for (var i = 0; i < spans.length;i++) {
21902                     se = spans[i];
21903                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
21904                         ff = se.style.fontFamily;
21905                         break;
21906                     }
21907                 }
21908                  
21909                     
21910                 //Roo.log("got font family: " + ff);
21911                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
21912                     listtype = 'ol';
21913                 }
21914                 
21915                 continue;
21916             }
21917             //Roo.log("no mso-list?");
21918             
21919             var spans = ns.getElementsByTagName('span');
21920             if (!spans.length) {
21921                 break;
21922             }
21923             var has_list  = false;
21924             for(var i = 0; i < spans.length; i++) {
21925                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
21926                     has_list = true;
21927                     break;
21928                 }
21929             }
21930             if (!has_list) {
21931                 break;
21932             }
21933             items.push(ns);
21934             ns = ns.nextSibling;
21935             
21936             
21937         }
21938         if (!items.length) {
21939             ns.className = "";
21940             return;
21941         }
21942         
21943         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
21944         parent.insertBefore(ul, p);
21945         var lvl = 0;
21946         var stack = [ ul ];
21947         var last_li = false;
21948         
21949         var margin_to_depth = {};
21950         max_margins = -1;
21951         
21952         items.forEach(function(n, ipos) {
21953             //Roo.log("got innertHMLT=" + n.innerHTML);
21954             
21955             var spans = n.getElementsByTagName('span');
21956             if (!spans.length) {
21957                 //Roo.log("No spans found");
21958                  
21959                 parent.removeChild(n);
21960                 
21961                 
21962                 return; // skip it...
21963             }
21964            
21965                 
21966             var num = 1;
21967             var style = {};
21968             for(var i = 0; i < spans.length; i++) {
21969             
21970                 style = this.styleToObject(spans[i]);
21971                 if (typeof(style['mso-list']) == 'undefined') {
21972                     continue;
21973                 }
21974                 if (listtype == 'ol') {
21975                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
21976                 }
21977                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
21978                 break;
21979             }
21980             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
21981             style = this.styleToObject(n); // mo-list is from the parent node.
21982             if (typeof(style['mso-list']) == 'undefined') {
21983                 //Roo.log("parent is missing level");
21984                   
21985                 parent.removeChild(n);
21986                  
21987                 return;
21988             }
21989             
21990             var margin = style['margin-left'];
21991             if (typeof(margin_to_depth[margin]) == 'undefined') {
21992                 max_margins++;
21993                 margin_to_depth[margin] = max_margins;
21994             }
21995             nlvl = margin_to_depth[margin] ;
21996              
21997             if (nlvl > lvl) {
21998                 //new indent
21999                 var nul = doc.createElement(listtype); // what about number lists...
22000                 if (!last_li) {
22001                     last_li = doc.createElement('li');
22002                     stack[lvl].appendChild(last_li);
22003                 }
22004                 last_li.appendChild(nul);
22005                 stack[nlvl] = nul;
22006                 
22007             }
22008             lvl = nlvl;
22009             
22010             // not starting at 1..
22011             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
22012                 stack[nlvl].setAttribute("start", num);
22013             }
22014             
22015             var nli = stack[nlvl].appendChild(doc.createElement('li'));
22016             last_li = nli;
22017             nli.innerHTML = n.innerHTML;
22018             //Roo.log("innerHTML = " + n.innerHTML);
22019             parent.removeChild(n);
22020             
22021              
22022              
22023             
22024         },this);
22025         
22026         
22027         
22028         
22029     },
22030     
22031     replaceImageTable : function(doc)
22032     {
22033          /*
22034           <table cellpadding=0 cellspacing=0 align=left>
22035   <tr>
22036    <td width=423 height=0></td>
22037   </tr>
22038   <tr>
22039    <td></td>
22040    <td><img width=601 height=401
22041    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
22042    v:shapes="Picture_x0020_2"></td>
22043   </tr>
22044  </table>
22045  */
22046         var imgs = Array.from(doc.getElementsByTagName('img'));
22047         Roo.each(imgs, function(img) {
22048             var td = img.parentNode;
22049             if (td.nodeName !=  'TD') {
22050                 return;
22051             }
22052             var tr = td.parentNode;
22053             if (tr.nodeName !=  'TR') {
22054                 return;
22055             }
22056             var tbody = tr.parentNode;
22057             if (tbody.nodeName !=  'TBODY') {
22058                 return;
22059             }
22060             var table = tbody.parentNode;
22061             if (table.nodeName !=  'TABLE') {
22062                 return;
22063             }
22064             // first row..
22065             
22066             if (table.getElementsByTagName('tr').length != 2) {
22067                 return;
22068             }
22069             if (table.getElementsByTagName('td').length != 3) {
22070                 return;
22071             }
22072             if (table.innerText.trim() != '') {
22073                 return;
22074             }
22075             var p = table.parentNode;
22076             img.parentNode.removeChild(img);
22077             p.insertBefore(img, table);
22078             p.removeChild(table);
22079             
22080             
22081             
22082         });
22083         
22084       
22085     }
22086     
22087 });
22088 /**
22089  * @class Roo.htmleditor.FilterStyleToTag
22090  * part of the word stuff... - certain 'styles' should be converted to tags.
22091  * eg.
22092  *   font-weight: bold -> bold
22093  *   ?? super / subscrit etc..
22094  * 
22095  * @constructor
22096 * Run a new style to tag filter.
22097 * @param {Object} config Configuration options
22098  */
22099 Roo.htmleditor.FilterStyleToTag = function(cfg)
22100 {
22101     
22102     this.tags = {
22103         B  : [ 'fontWeight' , 'bold'],
22104         I :  [ 'fontStyle' , 'italic'],
22105         //pre :  [ 'font-style' , 'italic'],
22106         // h1.. h6 ?? font-size?
22107         SUP : [ 'verticalAlign' , 'super' ],
22108         SUB : [ 'verticalAlign' , 'sub' ]
22109         
22110         
22111     };
22112     
22113     Roo.apply(this, cfg);
22114      
22115     
22116     this.walk(cfg.node);
22117     
22118     
22119     
22120 }
22121
22122
22123 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
22124 {
22125     tag: true, // all tags
22126     
22127     tags : false,
22128     
22129     
22130     replaceTag : function(node)
22131     {
22132         
22133         
22134         if (node.getAttribute("style") === null) {
22135             return true;
22136         }
22137         var inject = [];
22138         for (var k in this.tags) {
22139             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
22140                 inject.push(k);
22141                 node.style.removeProperty(this.tags[k][0]);
22142             }
22143         }
22144         if (!inject.length) {
22145             return true; 
22146         }
22147         var cn = Array.from(node.childNodes);
22148         var nn = node;
22149         Roo.each(inject, function(t) {
22150             var nc = node.ownerDocument.createElement(t);
22151             nn.appendChild(nc);
22152             nn = nc;
22153         });
22154         for(var i = 0;i < cn.length;cn++) {
22155             node.removeChild(cn[i]);
22156             nn.appendChild(cn[i]);
22157         }
22158         return true /// iterate thru
22159     }
22160     
22161 })/**
22162  * @class Roo.htmleditor.FilterLongBr
22163  * BR/BR/BR - keep a maximum of 2...
22164  * @constructor
22165  * Run a new Long BR Filter
22166  * @param {Object} config Configuration options
22167  */
22168
22169 Roo.htmleditor.FilterLongBr = function(cfg)
22170 {
22171     // no need to apply config.
22172     this.walk(cfg.node);
22173 }
22174
22175 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
22176 {
22177     
22178      
22179     tag : 'BR',
22180     
22181      
22182     replaceTag : function(node)
22183     {
22184         
22185         var ps = node.nextSibling;
22186         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
22187             ps = ps.nextSibling;
22188         }
22189         
22190         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
22191             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
22192             return false;
22193         }
22194         
22195         if (!ps || ps.nodeType != 1) {
22196             return false;
22197         }
22198         
22199         if (!ps || ps.tagName != 'BR') {
22200            
22201             return false;
22202         }
22203         
22204         
22205         
22206         
22207         
22208         if (!node.previousSibling) {
22209             return false;
22210         }
22211         var ps = node.previousSibling;
22212         
22213         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
22214             ps = ps.previousSibling;
22215         }
22216         if (!ps || ps.nodeType != 1) {
22217             return false;
22218         }
22219         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
22220         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
22221             return false;
22222         }
22223         
22224         node.parentNode.removeChild(node); // remove me...
22225         
22226         return false; // no need to do children
22227
22228     }
22229     
22230 }); 
22231
22232 /**
22233  * @class Roo.htmleditor.FilterBlock
22234  * removes id / data-block and contenteditable that are associated with blocks
22235  * usage should be done on a cloned copy of the dom
22236  * @constructor
22237 * Run a new Attribute Filter { node : xxxx }}
22238 * @param {Object} config Configuration options
22239  */
22240 Roo.htmleditor.FilterBlock = function(cfg)
22241 {
22242     Roo.apply(this, cfg);
22243     var qa = cfg.node.querySelectorAll;
22244     this.removeAttributes('data-block');
22245     this.removeAttributes('contenteditable');
22246     this.removeAttributes('id');
22247     
22248 }
22249
22250 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
22251 {
22252     node: true, // all tags
22253      
22254      
22255     removeAttributes : function(attr)
22256     {
22257         var ar = this.node.querySelectorAll('*[' + attr + ']');
22258         for (var i =0;i<ar.length;i++) {
22259             ar[i].removeAttribute(attr);
22260         }
22261     }
22262         
22263         
22264         
22265     
22266 });
22267 /***
22268  * This is based loosely on tinymce 
22269  * @class Roo.htmleditor.TidySerializer
22270  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22271  * @constructor
22272  * @method Serializer
22273  * @param {Object} settings Name/value settings object.
22274  */
22275
22276
22277 Roo.htmleditor.TidySerializer = function(settings)
22278 {
22279     Roo.apply(this, settings);
22280     
22281     this.writer = new Roo.htmleditor.TidyWriter(settings);
22282     
22283     
22284
22285 };
22286 Roo.htmleditor.TidySerializer.prototype = {
22287     
22288     /**
22289      * @param {boolean} inner do the inner of the node.
22290      */
22291     inner : false,
22292     
22293     writer : false,
22294     
22295     /**
22296     * Serializes the specified node into a string.
22297     *
22298     * @example
22299     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
22300     * @method serialize
22301     * @param {DomElement} node Node instance to serialize.
22302     * @return {String} String with HTML based on DOM tree.
22303     */
22304     serialize : function(node) {
22305         
22306         // = settings.validate;
22307         var writer = this.writer;
22308         var self  = this;
22309         this.handlers = {
22310             // #text
22311             3: function(node) {
22312                 
22313                 writer.text(node.nodeValue, node);
22314             },
22315             // #comment
22316             8: function(node) {
22317                 writer.comment(node.nodeValue);
22318             },
22319             // Processing instruction
22320             7: function(node) {
22321                 writer.pi(node.name, node.nodeValue);
22322             },
22323             // Doctype
22324             10: function(node) {
22325                 writer.doctype(node.nodeValue);
22326             },
22327             // CDATA
22328             4: function(node) {
22329                 writer.cdata(node.nodeValue);
22330             },
22331             // Document fragment
22332             11: function(node) {
22333                 node = node.firstChild;
22334                 if (!node) {
22335                     return;
22336                 }
22337                 while(node) {
22338                     self.walk(node);
22339                     node = node.nextSibling
22340                 }
22341             }
22342         };
22343         writer.reset();
22344         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
22345         return writer.getContent();
22346     },
22347
22348     walk: function(node)
22349     {
22350         var attrName, attrValue, sortedAttrs, i, l, elementRule,
22351             handler = this.handlers[node.nodeType];
22352             
22353         if (handler) {
22354             handler(node);
22355             return;
22356         }
22357     
22358         var name = node.nodeName;
22359         var isEmpty = node.childNodes.length < 1;
22360       
22361         var writer = this.writer;
22362         var attrs = node.attributes;
22363         // Sort attributes
22364         
22365         writer.start(node.nodeName, attrs, isEmpty, node);
22366         if (isEmpty) {
22367             return;
22368         }
22369         node = node.firstChild;
22370         if (!node) {
22371             writer.end(name);
22372             return;
22373         }
22374         while (node) {
22375             this.walk(node);
22376             node = node.nextSibling;
22377         }
22378         writer.end(name);
22379         
22380     
22381     }
22382     // Serialize element and treat all non elements as fragments
22383    
22384 }; 
22385
22386 /***
22387  * This is based loosely on tinymce 
22388  * @class Roo.htmleditor.TidyWriter
22389  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22390  *
22391  * Known issues?
22392  * - not tested much with 'PRE' formated elements.
22393  * 
22394  *
22395  *
22396  */
22397
22398 Roo.htmleditor.TidyWriter = function(settings)
22399 {
22400     
22401     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
22402     Roo.apply(this, settings);
22403     this.html = [];
22404     this.state = [];
22405      
22406     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
22407   
22408 }
22409 Roo.htmleditor.TidyWriter.prototype = {
22410
22411  
22412     state : false,
22413     
22414     indent :  '  ',
22415     
22416     // part of state...
22417     indentstr : '',
22418     in_pre: false,
22419     in_inline : false,
22420     last_inline : false,
22421     encode : false,
22422      
22423     
22424             /**
22425     * Writes the a start element such as <p id="a">.
22426     *
22427     * @method start
22428     * @param {String} name Name of the element.
22429     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22430     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22431     */
22432     start: function(name, attrs, empty, node)
22433     {
22434         var i, l, attr, value;
22435         
22436         // there are some situations where adding line break && indentation will not work. will not work.
22437         // <span / b / i ... formating?
22438         
22439         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22440         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22441         
22442         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22443         
22444         var add_lb = name == 'BR' ? false : in_inline;
22445         
22446         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22447             i_inline = false;
22448         }
22449
22450         var indentstr =  this.indentstr;
22451         
22452         // e_inline = elements that can be inline, but still allow \n before and after?
22453         // only 'BR' ??? any others?
22454         
22455         // ADD LINE BEFORE tage
22456         if (!this.in_pre) {
22457             if (in_inline) {
22458                 //code
22459                 if (name == 'BR') {
22460                     this.addLine();
22461                 } else if (this.lastElementEndsWS()) {
22462                     this.addLine();
22463                 } else{
22464                     // otherwise - no new line. (and dont indent.)
22465                     indentstr = '';
22466                 }
22467                 
22468             } else {
22469                 this.addLine();
22470             }
22471         } else {
22472             indentstr = '';
22473         }
22474         
22475         this.html.push(indentstr + '<', name.toLowerCase());
22476         
22477         if (attrs) {
22478             for (i = 0, l = attrs.length; i < l; i++) {
22479                 attr = attrs[i];
22480                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22481             }
22482         }
22483      
22484         if (empty) {
22485             if (is_short) {
22486                 this.html[this.html.length] = '/>';
22487             } else {
22488                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22489             }
22490             var e_inline = name == 'BR' ? false : this.in_inline;
22491             
22492             if (!e_inline && !this.in_pre) {
22493                 this.addLine();
22494             }
22495             return;
22496         
22497         }
22498         // not empty..
22499         this.html[this.html.length] = '>';
22500         
22501         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22502         /*
22503         if (!in_inline && !in_pre) {
22504             var cn = node.firstChild;
22505             while(cn) {
22506                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22507                     in_inline = true
22508                     break;
22509                 }
22510                 cn = cn.nextSibling;
22511             }
22512              
22513         }
22514         */
22515         
22516         
22517         this.pushState({
22518             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
22519             in_pre : in_pre,
22520             in_inline :  in_inline
22521         });
22522         // add a line after if we are not in a
22523         
22524         if (!in_inline && !in_pre) {
22525             this.addLine();
22526         }
22527         
22528             
22529          
22530         
22531     },
22532     
22533     lastElementEndsWS : function()
22534     {
22535         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22536         if (value === false) {
22537             return true;
22538         }
22539         return value.match(/\s+$/);
22540         
22541     },
22542     
22543     /**
22544      * Writes the a end element such as </p>.
22545      *
22546      * @method end
22547      * @param {String} name Name of the element.
22548      */
22549     end: function(name) {
22550         var value;
22551         this.popState();
22552         var indentstr = '';
22553         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22554         
22555         if (!this.in_pre && !in_inline) {
22556             this.addLine();
22557             indentstr  = this.indentstr;
22558         }
22559         this.html.push(indentstr + '</', name.toLowerCase(), '>');
22560         this.last_inline = in_inline;
22561         
22562         // pop the indent state..
22563     },
22564     /**
22565      * Writes a text node.
22566      *
22567      * In pre - we should not mess with the contents.
22568      * 
22569      *
22570      * @method text
22571      * @param {String} text String to write out.
22572      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22573      */
22574     text: function(in_text, node)
22575     {
22576         // if not in whitespace critical
22577         if (in_text.length < 1) {
22578             return;
22579         }
22580         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
22581         
22582         if (this.in_pre) {
22583             this.html[this.html.length] =  text;
22584             return;   
22585         }
22586         
22587         if (this.in_inline) {
22588             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22589             if (text != ' ') {
22590                 text = text.replace(/\s+/,' ');  // all white space to single white space
22591                 
22592                     
22593                 // if next tag is '<BR>', then we can trim right..
22594                 if (node.nextSibling &&
22595                     node.nextSibling.nodeType == 1 &&
22596                     node.nextSibling.nodeName == 'BR' )
22597                 {
22598                     text = text.replace(/\s+$/g,'');
22599                 }
22600                 // if previous tag was a BR, we can also trim..
22601                 if (node.previousSibling &&
22602                     node.previousSibling.nodeType == 1 &&
22603                     node.previousSibling.nodeName == 'BR' )
22604                 {
22605                     text = this.indentstr +  text.replace(/^\s+/g,'');
22606                 }
22607                 if (text.match(/\n/)) {
22608                     text = text.replace(
22609                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22610                     );
22611                     // remoeve the last whitespace / line break.
22612                     text = text.replace(/\n\s+$/,'');
22613                 }
22614                 // repace long lines
22615                 
22616             }
22617              
22618             this.html[this.html.length] =  text;
22619             return;   
22620         }
22621         // see if previous element was a inline element.
22622         var indentstr = this.indentstr;
22623    
22624         text = text.replace(/\s+/g," "); // all whitespace into single white space.
22625         
22626         // should trim left?
22627         if (node.previousSibling &&
22628             node.previousSibling.nodeType == 1 &&
22629             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22630         {
22631             indentstr = '';
22632             
22633         } else {
22634             this.addLine();
22635             text = text.replace(/^\s+/,''); // trim left
22636           
22637         }
22638         // should trim right?
22639         if (node.nextSibling &&
22640             node.nextSibling.nodeType == 1 &&
22641             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22642         {
22643           // noop
22644             
22645         }  else {
22646             text = text.replace(/\s+$/,''); // trim right
22647         }
22648          
22649               
22650         
22651         
22652         
22653         if (text.length < 1) {
22654             return;
22655         }
22656         if (!text.match(/\n/)) {
22657             this.html.push(indentstr + text);
22658             return;
22659         }
22660         
22661         text = this.indentstr + text.replace(
22662             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22663         );
22664         // remoeve the last whitespace / line break.
22665         text = text.replace(/\s+$/,''); 
22666         
22667         this.html.push(text);
22668         
22669         // split and indent..
22670         
22671         
22672     },
22673     /**
22674      * Writes a cdata node such as <![CDATA[data]]>.
22675      *
22676      * @method cdata
22677      * @param {String} text String to write out inside the cdata.
22678      */
22679     cdata: function(text) {
22680         this.html.push('<![CDATA[', text, ']]>');
22681     },
22682     /**
22683     * Writes a comment node such as <!-- Comment -->.
22684     *
22685     * @method cdata
22686     * @param {String} text String to write out inside the comment.
22687     */
22688    comment: function(text) {
22689        this.html.push('<!--', text, '-->');
22690    },
22691     /**
22692      * Writes a PI node such as <?xml attr="value" ?>.
22693      *
22694      * @method pi
22695      * @param {String} name Name of the pi.
22696      * @param {String} text String to write out inside the pi.
22697      */
22698     pi: function(name, text) {
22699         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22700         this.indent != '' && this.html.push('\n');
22701     },
22702     /**
22703      * Writes a doctype node such as <!DOCTYPE data>.
22704      *
22705      * @method doctype
22706      * @param {String} text String to write out inside the doctype.
22707      */
22708     doctype: function(text) {
22709         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22710     },
22711     /**
22712      * Resets the internal buffer if one wants to reuse the writer.
22713      *
22714      * @method reset
22715      */
22716     reset: function() {
22717         this.html.length = 0;
22718         this.state = [];
22719         this.pushState({
22720             indentstr : '',
22721             in_pre : false, 
22722             in_inline : false
22723         })
22724     },
22725     /**
22726      * Returns the contents that got serialized.
22727      *
22728      * @method getContent
22729      * @return {String} HTML contents that got written down.
22730      */
22731     getContent: function() {
22732         return this.html.join('').replace(/\n$/, '');
22733     },
22734     
22735     pushState : function(cfg)
22736     {
22737         this.state.push(cfg);
22738         Roo.apply(this, cfg);
22739     },
22740     
22741     popState : function()
22742     {
22743         if (this.state.length < 1) {
22744             return; // nothing to push
22745         }
22746         var cfg = {
22747             in_pre: false,
22748             indentstr : ''
22749         };
22750         this.state.pop();
22751         if (this.state.length > 0) {
22752             cfg = this.state[this.state.length-1]; 
22753         }
22754         Roo.apply(this, cfg);
22755     },
22756     
22757     addLine: function()
22758     {
22759         if (this.html.length < 1) {
22760             return;
22761         }
22762         
22763         
22764         var value = this.html[this.html.length - 1];
22765         if (value.length > 0 && '\n' !== value) {
22766             this.html.push('\n');
22767         }
22768     }
22769     
22770     
22771 //'pre script noscript style textarea video audio iframe object code'
22772 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
22773 // inline 
22774 };
22775
22776 Roo.htmleditor.TidyWriter.inline_elements = [
22777         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22778         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
22779 ];
22780 Roo.htmleditor.TidyWriter.shortend_elements = [
22781     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22782     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22783 ];
22784
22785 Roo.htmleditor.TidyWriter.whitespace_elements = [
22786     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22787 ];/***
22788  * This is based loosely on tinymce 
22789  * @class Roo.htmleditor.TidyEntities
22790  * @static
22791  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22792  *
22793  * Not 100% sure this is actually used or needed.
22794  */
22795
22796 Roo.htmleditor.TidyEntities = {
22797     
22798     /**
22799      * initialize data..
22800      */
22801     init : function (){
22802      
22803         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22804        
22805     },
22806
22807
22808     buildEntitiesLookup: function(items, radix) {
22809         var i, chr, entity, lookup = {};
22810         if (!items) {
22811             return {};
22812         }
22813         items = typeof(items) == 'string' ? items.split(',') : items;
22814         radix = radix || 10;
22815         // Build entities lookup table
22816         for (i = 0; i < items.length; i += 2) {
22817             chr = String.fromCharCode(parseInt(items[i], radix));
22818             // Only add non base entities
22819             if (!this.baseEntities[chr]) {
22820                 entity = '&' + items[i + 1] + ';';
22821                 lookup[chr] = entity;
22822                 lookup[entity] = chr;
22823             }
22824         }
22825         return lookup;
22826         
22827     },
22828     
22829     asciiMap : {
22830             128: '€',
22831             130: '‚',
22832             131: 'ƒ',
22833             132: '„',
22834             133: '…',
22835             134: '†',
22836             135: '‡',
22837             136: 'ˆ',
22838             137: '‰',
22839             138: 'Š',
22840             139: '‹',
22841             140: 'Œ',
22842             142: 'Ž',
22843             145: '‘',
22844             146: '’',
22845             147: '“',
22846             148: '”',
22847             149: '•',
22848             150: '–',
22849             151: '—',
22850             152: '˜',
22851             153: '™',
22852             154: 'š',
22853             155: '›',
22854             156: 'œ',
22855             158: 'ž',
22856             159: 'Ÿ'
22857     },
22858     // Raw entities
22859     baseEntities : {
22860         '"': '&quot;',
22861         // Needs to be escaped since the YUI compressor would otherwise break the code
22862         '\'': '&#39;',
22863         '<': '&lt;',
22864         '>': '&gt;',
22865         '&': '&amp;',
22866         '`': '&#96;'
22867     },
22868     // Reverse lookup table for raw entities
22869     reverseEntities : {
22870         '&lt;': '<',
22871         '&gt;': '>',
22872         '&amp;': '&',
22873         '&quot;': '"',
22874         '&apos;': '\''
22875     },
22876     
22877     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22878     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22879     rawCharsRegExp : /[<>&\"\']/g,
22880     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22881     namedEntities  : false,
22882     namedEntitiesData : [ 
22883         '50',
22884         'nbsp',
22885         '51',
22886         'iexcl',
22887         '52',
22888         'cent',
22889         '53',
22890         'pound',
22891         '54',
22892         'curren',
22893         '55',
22894         'yen',
22895         '56',
22896         'brvbar',
22897         '57',
22898         'sect',
22899         '58',
22900         'uml',
22901         '59',
22902         'copy',
22903         '5a',
22904         'ordf',
22905         '5b',
22906         'laquo',
22907         '5c',
22908         'not',
22909         '5d',
22910         'shy',
22911         '5e',
22912         'reg',
22913         '5f',
22914         'macr',
22915         '5g',
22916         'deg',
22917         '5h',
22918         'plusmn',
22919         '5i',
22920         'sup2',
22921         '5j',
22922         'sup3',
22923         '5k',
22924         'acute',
22925         '5l',
22926         'micro',
22927         '5m',
22928         'para',
22929         '5n',
22930         'middot',
22931         '5o',
22932         'cedil',
22933         '5p',
22934         'sup1',
22935         '5q',
22936         'ordm',
22937         '5r',
22938         'raquo',
22939         '5s',
22940         'frac14',
22941         '5t',
22942         'frac12',
22943         '5u',
22944         'frac34',
22945         '5v',
22946         'iquest',
22947         '60',
22948         'Agrave',
22949         '61',
22950         'Aacute',
22951         '62',
22952         'Acirc',
22953         '63',
22954         'Atilde',
22955         '64',
22956         'Auml',
22957         '65',
22958         'Aring',
22959         '66',
22960         'AElig',
22961         '67',
22962         'Ccedil',
22963         '68',
22964         'Egrave',
22965         '69',
22966         'Eacute',
22967         '6a',
22968         'Ecirc',
22969         '6b',
22970         'Euml',
22971         '6c',
22972         'Igrave',
22973         '6d',
22974         'Iacute',
22975         '6e',
22976         'Icirc',
22977         '6f',
22978         'Iuml',
22979         '6g',
22980         'ETH',
22981         '6h',
22982         'Ntilde',
22983         '6i',
22984         'Ograve',
22985         '6j',
22986         'Oacute',
22987         '6k',
22988         'Ocirc',
22989         '6l',
22990         'Otilde',
22991         '6m',
22992         'Ouml',
22993         '6n',
22994         'times',
22995         '6o',
22996         'Oslash',
22997         '6p',
22998         'Ugrave',
22999         '6q',
23000         'Uacute',
23001         '6r',
23002         'Ucirc',
23003         '6s',
23004         'Uuml',
23005         '6t',
23006         'Yacute',
23007         '6u',
23008         'THORN',
23009         '6v',
23010         'szlig',
23011         '70',
23012         'agrave',
23013         '71',
23014         'aacute',
23015         '72',
23016         'acirc',
23017         '73',
23018         'atilde',
23019         '74',
23020         'auml',
23021         '75',
23022         'aring',
23023         '76',
23024         'aelig',
23025         '77',
23026         'ccedil',
23027         '78',
23028         'egrave',
23029         '79',
23030         'eacute',
23031         '7a',
23032         'ecirc',
23033         '7b',
23034         'euml',
23035         '7c',
23036         'igrave',
23037         '7d',
23038         'iacute',
23039         '7e',
23040         'icirc',
23041         '7f',
23042         'iuml',
23043         '7g',
23044         'eth',
23045         '7h',
23046         'ntilde',
23047         '7i',
23048         'ograve',
23049         '7j',
23050         'oacute',
23051         '7k',
23052         'ocirc',
23053         '7l',
23054         'otilde',
23055         '7m',
23056         'ouml',
23057         '7n',
23058         'divide',
23059         '7o',
23060         'oslash',
23061         '7p',
23062         'ugrave',
23063         '7q',
23064         'uacute',
23065         '7r',
23066         'ucirc',
23067         '7s',
23068         'uuml',
23069         '7t',
23070         'yacute',
23071         '7u',
23072         'thorn',
23073         '7v',
23074         'yuml',
23075         'ci',
23076         'fnof',
23077         'sh',
23078         'Alpha',
23079         'si',
23080         'Beta',
23081         'sj',
23082         'Gamma',
23083         'sk',
23084         'Delta',
23085         'sl',
23086         'Epsilon',
23087         'sm',
23088         'Zeta',
23089         'sn',
23090         'Eta',
23091         'so',
23092         'Theta',
23093         'sp',
23094         'Iota',
23095         'sq',
23096         'Kappa',
23097         'sr',
23098         'Lambda',
23099         'ss',
23100         'Mu',
23101         'st',
23102         'Nu',
23103         'su',
23104         'Xi',
23105         'sv',
23106         'Omicron',
23107         't0',
23108         'Pi',
23109         't1',
23110         'Rho',
23111         't3',
23112         'Sigma',
23113         't4',
23114         'Tau',
23115         't5',
23116         'Upsilon',
23117         't6',
23118         'Phi',
23119         't7',
23120         'Chi',
23121         't8',
23122         'Psi',
23123         't9',
23124         'Omega',
23125         'th',
23126         'alpha',
23127         'ti',
23128         'beta',
23129         'tj',
23130         'gamma',
23131         'tk',
23132         'delta',
23133         'tl',
23134         'epsilon',
23135         'tm',
23136         'zeta',
23137         'tn',
23138         'eta',
23139         'to',
23140         'theta',
23141         'tp',
23142         'iota',
23143         'tq',
23144         'kappa',
23145         'tr',
23146         'lambda',
23147         'ts',
23148         'mu',
23149         'tt',
23150         'nu',
23151         'tu',
23152         'xi',
23153         'tv',
23154         'omicron',
23155         'u0',
23156         'pi',
23157         'u1',
23158         'rho',
23159         'u2',
23160         'sigmaf',
23161         'u3',
23162         'sigma',
23163         'u4',
23164         'tau',
23165         'u5',
23166         'upsilon',
23167         'u6',
23168         'phi',
23169         'u7',
23170         'chi',
23171         'u8',
23172         'psi',
23173         'u9',
23174         'omega',
23175         'uh',
23176         'thetasym',
23177         'ui',
23178         'upsih',
23179         'um',
23180         'piv',
23181         '812',
23182         'bull',
23183         '816',
23184         'hellip',
23185         '81i',
23186         'prime',
23187         '81j',
23188         'Prime',
23189         '81u',
23190         'oline',
23191         '824',
23192         'frasl',
23193         '88o',
23194         'weierp',
23195         '88h',
23196         'image',
23197         '88s',
23198         'real',
23199         '892',
23200         'trade',
23201         '89l',
23202         'alefsym',
23203         '8cg',
23204         'larr',
23205         '8ch',
23206         'uarr',
23207         '8ci',
23208         'rarr',
23209         '8cj',
23210         'darr',
23211         '8ck',
23212         'harr',
23213         '8dl',
23214         'crarr',
23215         '8eg',
23216         'lArr',
23217         '8eh',
23218         'uArr',
23219         '8ei',
23220         'rArr',
23221         '8ej',
23222         'dArr',
23223         '8ek',
23224         'hArr',
23225         '8g0',
23226         'forall',
23227         '8g2',
23228         'part',
23229         '8g3',
23230         'exist',
23231         '8g5',
23232         'empty',
23233         '8g7',
23234         'nabla',
23235         '8g8',
23236         'isin',
23237         '8g9',
23238         'notin',
23239         '8gb',
23240         'ni',
23241         '8gf',
23242         'prod',
23243         '8gh',
23244         'sum',
23245         '8gi',
23246         'minus',
23247         '8gn',
23248         'lowast',
23249         '8gq',
23250         'radic',
23251         '8gt',
23252         'prop',
23253         '8gu',
23254         'infin',
23255         '8h0',
23256         'ang',
23257         '8h7',
23258         'and',
23259         '8h8',
23260         'or',
23261         '8h9',
23262         'cap',
23263         '8ha',
23264         'cup',
23265         '8hb',
23266         'int',
23267         '8hk',
23268         'there4',
23269         '8hs',
23270         'sim',
23271         '8i5',
23272         'cong',
23273         '8i8',
23274         'asymp',
23275         '8j0',
23276         'ne',
23277         '8j1',
23278         'equiv',
23279         '8j4',
23280         'le',
23281         '8j5',
23282         'ge',
23283         '8k2',
23284         'sub',
23285         '8k3',
23286         'sup',
23287         '8k4',
23288         'nsub',
23289         '8k6',
23290         'sube',
23291         '8k7',
23292         'supe',
23293         '8kl',
23294         'oplus',
23295         '8kn',
23296         'otimes',
23297         '8l5',
23298         'perp',
23299         '8m5',
23300         'sdot',
23301         '8o8',
23302         'lceil',
23303         '8o9',
23304         'rceil',
23305         '8oa',
23306         'lfloor',
23307         '8ob',
23308         'rfloor',
23309         '8p9',
23310         'lang',
23311         '8pa',
23312         'rang',
23313         '9ea',
23314         'loz',
23315         '9j0',
23316         'spades',
23317         '9j3',
23318         'clubs',
23319         '9j5',
23320         'hearts',
23321         '9j6',
23322         'diams',
23323         'ai',
23324         'OElig',
23325         'aj',
23326         'oelig',
23327         'b0',
23328         'Scaron',
23329         'b1',
23330         'scaron',
23331         'bo',
23332         'Yuml',
23333         'm6',
23334         'circ',
23335         'ms',
23336         'tilde',
23337         '802',
23338         'ensp',
23339         '803',
23340         'emsp',
23341         '809',
23342         'thinsp',
23343         '80c',
23344         'zwnj',
23345         '80d',
23346         'zwj',
23347         '80e',
23348         'lrm',
23349         '80f',
23350         'rlm',
23351         '80j',
23352         'ndash',
23353         '80k',
23354         'mdash',
23355         '80o',
23356         'lsquo',
23357         '80p',
23358         'rsquo',
23359         '80q',
23360         'sbquo',
23361         '80s',
23362         'ldquo',
23363         '80t',
23364         'rdquo',
23365         '80u',
23366         'bdquo',
23367         '810',
23368         'dagger',
23369         '811',
23370         'Dagger',
23371         '81g',
23372         'permil',
23373         '81p',
23374         'lsaquo',
23375         '81q',
23376         'rsaquo',
23377         '85c',
23378         'euro'
23379     ],
23380
23381          
23382     /**
23383      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
23384      *
23385      * @method encodeRaw
23386      * @param {String} text Text to encode.
23387      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23388      * @return {String} Entity encoded text.
23389      */
23390     encodeRaw: function(text, attr)
23391     {
23392         var t = this;
23393         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23394             return t.baseEntities[chr] || chr;
23395         });
23396     },
23397     /**
23398      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
23399      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
23400      * and is exposed as the DOMUtils.encode function.
23401      *
23402      * @method encodeAllRaw
23403      * @param {String} text Text to encode.
23404      * @return {String} Entity encoded text.
23405      */
23406     encodeAllRaw: function(text) {
23407         var t = this;
23408         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
23409             return t.baseEntities[chr] || chr;
23410         });
23411     },
23412     /**
23413      * Encodes the specified string using numeric entities. The core entities will be
23414      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
23415      *
23416      * @method encodeNumeric
23417      * @param {String} text Text to encode.
23418      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23419      * @return {String} Entity encoded text.
23420      */
23421     encodeNumeric: function(text, attr) {
23422         var t = this;
23423         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23424             // Multi byte sequence convert it to a single entity
23425             if (chr.length > 1) {
23426                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
23427             }
23428             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23429         });
23430     },
23431     /**
23432      * Encodes the specified string using named entities. The core entities will be encoded
23433      * as named ones but all non lower ascii characters will be encoded into named entities.
23434      *
23435      * @method encodeNamed
23436      * @param {String} text Text to encode.
23437      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23438      * @param {Object} entities Optional parameter with entities to use.
23439      * @return {String} Entity encoded text.
23440      */
23441     encodeNamed: function(text, attr, entities) {
23442         var t = this;
23443         entities = entities || this.namedEntities;
23444         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23445             return t.baseEntities[chr] || entities[chr] || chr;
23446         });
23447     },
23448     /**
23449      * Returns an encode function based on the name(s) and it's optional entities.
23450      *
23451      * @method getEncodeFunc
23452      * @param {String} name Comma separated list of encoders for example named,numeric.
23453      * @param {String} entities Optional parameter with entities to use instead of the built in set.
23454      * @return {function} Encode function to be used.
23455      */
23456     getEncodeFunc: function(name, entities) {
23457         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23458         var t = this;
23459         function encodeNamedAndNumeric(text, attr) {
23460             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23461                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23462             });
23463         }
23464
23465         function encodeCustomNamed(text, attr) {
23466             return t.encodeNamed(text, attr, entities);
23467         }
23468         // Replace + with , to be compatible with previous TinyMCE versions
23469         name = this.makeMap(name.replace(/\+/g, ','));
23470         // Named and numeric encoder
23471         if (name.named && name.numeric) {
23472             return this.encodeNamedAndNumeric;
23473         }
23474         // Named encoder
23475         if (name.named) {
23476             // Custom names
23477             if (entities) {
23478                 return encodeCustomNamed;
23479             }
23480             return this.encodeNamed;
23481         }
23482         // Numeric
23483         if (name.numeric) {
23484             return this.encodeNumeric;
23485         }
23486         // Raw encoder
23487         return this.encodeRaw;
23488     },
23489     /**
23490      * Decodes the specified string, this will replace entities with raw UTF characters.
23491      *
23492      * @method decode
23493      * @param {String} text Text to entity decode.
23494      * @return {String} Entity decoded string.
23495      */
23496     decode: function(text)
23497     {
23498         var  t = this;
23499         return text.replace(this.entityRegExp, function(all, numeric) {
23500             if (numeric) {
23501                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23502                 // Support upper UTF
23503                 if (numeric > 65535) {
23504                     numeric -= 65536;
23505                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23506                 }
23507                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23508             }
23509             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23510         });
23511     },
23512     nativeDecode : function (text) {
23513         return text;
23514     },
23515     makeMap : function (items, delim, map) {
23516                 var i;
23517                 items = items || [];
23518                 delim = delim || ',';
23519                 if (typeof items == "string") {
23520                         items = items.split(delim);
23521                 }
23522                 map = map || {};
23523                 i = items.length;
23524                 while (i--) {
23525                         map[items[i]] = {};
23526                 }
23527                 return map;
23528         }
23529 };
23530     
23531     
23532     
23533 Roo.htmleditor.TidyEntities.init();
23534 /**
23535  * @class Roo.htmleditor.KeyEnter
23536  * Handle Enter press..
23537  * @cfg {Roo.HtmlEditorCore} core the editor.
23538  * @constructor
23539  * Create a new Filter.
23540  * @param {Object} config Configuration options
23541  */
23542
23543
23544
23545
23546
23547 Roo.htmleditor.KeyEnter = function(cfg) {
23548     Roo.apply(this, cfg);
23549     // this does not actually call walk as it's really just a abstract class
23550  
23551     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23552 }
23553
23554 //Roo.htmleditor.KeyEnter.i = 0;
23555
23556
23557 Roo.htmleditor.KeyEnter.prototype = {
23558     
23559     core : false,
23560     
23561     keypress : function(e)
23562     {
23563         if (e.charCode != 13 && e.charCode != 10) {
23564             Roo.log([e.charCode,e]);
23565             return true;
23566         }
23567         e.preventDefault();
23568         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23569         var doc = this.core.doc;
23570           //add a new line
23571        
23572     
23573         var sel = this.core.getSelection();
23574         var range = sel.getRangeAt(0);
23575         var n = range.commonAncestorContainer;
23576         var pc = range.closest([ 'ol', 'ul']);
23577         var pli = range.closest('li');
23578         if (!pc || e.ctrlKey) {
23579             // on it list, or ctrl pressed.
23580             if (!e.ctrlKey) {
23581                 sel.insertNode('br', 'after'); 
23582             } else {
23583                 // only do this if we have ctrl key..
23584                 var br = doc.createElement('br');
23585                 br.className = 'clear';
23586                 br.setAttribute('style', 'clear: both');
23587                 sel.insertNode(br, 'after'); 
23588             }
23589             
23590          
23591             this.core.undoManager.addEvent();
23592             this.core.fireEditorEvent(e);
23593             return false;
23594         }
23595         
23596         // deal with <li> insetion
23597         if (pli.innerText.trim() == '' &&
23598             pli.previousSibling &&
23599             pli.previousSibling.nodeName == 'LI' &&
23600             pli.previousSibling.innerText.trim() ==  '') {
23601             pli.parentNode.removeChild(pli.previousSibling);
23602             sel.cursorAfter(pc);
23603             this.core.undoManager.addEvent();
23604             this.core.fireEditorEvent(e);
23605             return false;
23606         }
23607     
23608         var li = doc.createElement('LI');
23609         li.innerHTML = '&nbsp;';
23610         if (!pli || !pli.firstSibling) {
23611             pc.appendChild(li);
23612         } else {
23613             pli.parentNode.insertBefore(li, pli.firstSibling);
23614         }
23615         sel.cursorText (li.firstChild);
23616       
23617         this.core.undoManager.addEvent();
23618         this.core.fireEditorEvent(e);
23619
23620         return false;
23621         
23622     
23623         
23624         
23625          
23626     }
23627 };
23628      
23629 /**
23630  * @class Roo.htmleditor.Block
23631  * Base class for html editor blocks - do not use it directly .. extend it..
23632  * @cfg {DomElement} node The node to apply stuff to.
23633  * @cfg {String} friendly_name the name that appears in the context bar about this block
23634  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23635  
23636  * @constructor
23637  * Create a new Filter.
23638  * @param {Object} config Configuration options
23639  */
23640
23641 Roo.htmleditor.Block  = function(cfg)
23642 {
23643     // do nothing .. should not be called really.
23644 }
23645 /**
23646  * factory method to get the block from an element (using cache if necessary)
23647  * @static
23648  * @param {HtmlElement} the dom element
23649  */
23650 Roo.htmleditor.Block.factory = function(node)
23651 {
23652     var cc = Roo.htmleditor.Block.cache;
23653     var id = Roo.get(node).id;
23654     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23655         Roo.htmleditor.Block.cache[id].readElement(node);
23656         return Roo.htmleditor.Block.cache[id];
23657     }
23658     var db  = node.getAttribute('data-block');
23659     if (!db) {
23660         db = node.nodeName.toLowerCase().toUpperCaseFirst();
23661     }
23662     var cls = Roo.htmleditor['Block' + db];
23663     if (typeof(cls) == 'undefined') {
23664         //Roo.log(node.getAttribute('data-block'));
23665         Roo.log("OOps missing block : " + 'Block' + db);
23666         return false;
23667     }
23668     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23669     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
23670 };
23671
23672 /**
23673  * initalize all Elements from content that are 'blockable'
23674  * @static
23675  * @param the body element
23676  */
23677 Roo.htmleditor.Block.initAll = function(body, type)
23678 {
23679     if (typeof(type) == 'undefined') {
23680         var ia = Roo.htmleditor.Block.initAll;
23681         ia(body,'table');
23682         ia(body,'td');
23683         ia(body,'figure');
23684         return;
23685     }
23686     Roo.each(Roo.get(body).query(type), function(e) {
23687         Roo.htmleditor.Block.factory(e);    
23688     },this);
23689 };
23690 // question goes here... do we need to clear out this cache sometimes?
23691 // or show we make it relivant to the htmleditor.
23692 Roo.htmleditor.Block.cache = {};
23693
23694 Roo.htmleditor.Block.prototype = {
23695     
23696     node : false,
23697     
23698      // used by context menu
23699     friendly_name : 'Based Block',
23700     
23701     // text for button to delete this element
23702     deleteTitle : false,
23703     
23704     context : false,
23705     /**
23706      * Update a node with values from this object
23707      * @param {DomElement} node
23708      */
23709     updateElement : function(node)
23710     {
23711         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23712     },
23713      /**
23714      * convert to plain HTML for calling insertAtCursor..
23715      */
23716     toHTML : function()
23717     {
23718         return Roo.DomHelper.markup(this.toObject());
23719     },
23720     /**
23721      * used by readEleemnt to extract data from a node
23722      * may need improving as it's pretty basic
23723      
23724      * @param {DomElement} node
23725      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23726      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
23727      * @param {String} style the style property - eg. text-align
23728      */
23729     getVal : function(node, tag, attr, style)
23730     {
23731         var n = node;
23732         if (tag !== true && n.tagName != tag.toUpperCase()) {
23733             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23734             // but kiss for now.
23735             n = node.getElementsByTagName(tag).item(0);
23736         }
23737         if (!n) {
23738             return '';
23739         }
23740         if (attr === false) {
23741             return n;
23742         }
23743         if (attr == 'html') {
23744             return n.innerHTML;
23745         }
23746         if (attr == 'style') {
23747             return n.style[style]; 
23748         }
23749         
23750         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23751             
23752     },
23753     /**
23754      * create a DomHelper friendly object - for use with 
23755      * Roo.DomHelper.markup / overwrite / etc..
23756      * (override this)
23757      */
23758     toObject : function()
23759     {
23760         return {};
23761     },
23762       /**
23763      * Read a node that has a 'data-block' property - and extract the values from it.
23764      * @param {DomElement} node - the node
23765      */
23766     readElement : function(node)
23767     {
23768         
23769     } 
23770     
23771     
23772 };
23773
23774  
23775
23776 /**
23777  * @class Roo.htmleditor.BlockFigure
23778  * Block that has an image and a figcaption
23779  * @cfg {String} image_src the url for the image
23780  * @cfg {String} align (left|right) alignment for the block default left
23781  * @cfg {String} caption the text to appear below  (and in the alt tag)
23782  * @cfg {String} caption_display (block|none) display or not the caption
23783  * @cfg {String|number} image_width the width of the image number or %?
23784  * @cfg {String|number} image_height the height of the image number or %?
23785  * 
23786  * @constructor
23787  * Create a new Filter.
23788  * @param {Object} config Configuration options
23789  */
23790
23791 Roo.htmleditor.BlockFigure = function(cfg)
23792 {
23793     if (cfg.node) {
23794         this.readElement(cfg.node);
23795         this.updateElement(cfg.node);
23796     }
23797     Roo.apply(this, cfg);
23798 }
23799 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23800  
23801     
23802     // setable values.
23803     image_src: '',
23804     align: 'center',
23805     caption : '',
23806     caption_display : 'block',
23807     width : '100%',
23808     cls : '',
23809     href: '',
23810     video_url : '',
23811     
23812     // margin: '2%', not used
23813     
23814     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
23815
23816     
23817     // used by context menu
23818     friendly_name : 'Image with caption',
23819     deleteTitle : "Delete Image and Caption",
23820     
23821     contextMenu : function(toolbar)
23822     {
23823         
23824         var block = function() {
23825             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23826         };
23827         
23828         
23829         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23830         
23831         var syncValue = toolbar.editorcore.syncValue;
23832         
23833         var fields = {};
23834         
23835         return [
23836              {
23837                 xtype : 'TextItem',
23838                 text : "Source: ",
23839                 xns : rooui.Toolbar  //Boostrap?
23840             },
23841             {
23842                 xtype : 'Button',
23843                 text: 'Change Image URL',
23844                  
23845                 listeners : {
23846                     click: function (btn, state)
23847                     {
23848                         var b = block();
23849                         
23850                         Roo.MessageBox.show({
23851                             title : "Image Source URL",
23852                             msg : "Enter the url for the image",
23853                             buttons: Roo.MessageBox.OKCANCEL,
23854                             fn: function(btn, val){
23855                                 if (btn != 'ok') {
23856                                     return;
23857                                 }
23858                                 b.image_src = val;
23859                                 b.updateElement();
23860                                 syncValue();
23861                                 toolbar.editorcore.onEditorEvent();
23862                             },
23863                             minWidth:250,
23864                             prompt:true,
23865                             //multiline: multiline,
23866                             modal : true,
23867                             value : b.image_src
23868                         });
23869                     }
23870                 },
23871                 xns : rooui.Toolbar
23872             },
23873          
23874             {
23875                 xtype : 'Button',
23876                 text: 'Change Link URL',
23877                  
23878                 listeners : {
23879                     click: function (btn, state)
23880                     {
23881                         var b = block();
23882                         
23883                         Roo.MessageBox.show({
23884                             title : "Link URL",
23885                             msg : "Enter the url for the link - leave blank to have no link",
23886                             buttons: Roo.MessageBox.OKCANCEL,
23887                             fn: function(btn, val){
23888                                 if (btn != 'ok') {
23889                                     return;
23890                                 }
23891                                 b.href = val;
23892                                 b.updateElement();
23893                                 syncValue();
23894                                 toolbar.editorcore.onEditorEvent();
23895                             },
23896                             minWidth:250,
23897                             prompt:true,
23898                             //multiline: multiline,
23899                             modal : true,
23900                             value : b.href
23901                         });
23902                     }
23903                 },
23904                 xns : rooui.Toolbar
23905             },
23906             {
23907                 xtype : 'Button',
23908                 text: 'Show Video URL',
23909                  
23910                 listeners : {
23911                     click: function (btn, state)
23912                     {
23913                         Roo.MessageBox.alert("Video URL",
23914                             block().video_url == '' ? 'This image is not linked ot a video' :
23915                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23916                     }
23917                 },
23918                 xns : rooui.Toolbar
23919             },
23920             
23921             
23922             {
23923                 xtype : 'TextItem',
23924                 text : "Width: ",
23925                 xns : rooui.Toolbar  //Boostrap?
23926             },
23927             {
23928                 xtype : 'ComboBox',
23929                 allowBlank : false,
23930                 displayField : 'val',
23931                 editable : true,
23932                 listWidth : 100,
23933                 triggerAction : 'all',
23934                 typeAhead : true,
23935                 valueField : 'val',
23936                 width : 70,
23937                 name : 'width',
23938                 listeners : {
23939                     select : function (combo, r, index)
23940                     {
23941                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23942                         var b = block();
23943                         b.width = r.get('val');
23944                         b.updateElement();
23945                         syncValue();
23946                         toolbar.editorcore.onEditorEvent();
23947                     }
23948                 },
23949                 xns : rooui.form,
23950                 store : {
23951                     xtype : 'SimpleStore',
23952                     data : [
23953                         ['100%'],
23954                         ['80%'],
23955                         ['50%'],
23956                         ['20%'],
23957                         ['10%']
23958                     ],
23959                     fields : [ 'val'],
23960                     xns : Roo.data
23961                 }
23962             },
23963             {
23964                 xtype : 'TextItem',
23965                 text : "Align: ",
23966                 xns : rooui.Toolbar  //Boostrap?
23967             },
23968             {
23969                 xtype : 'ComboBox',
23970                 allowBlank : false,
23971                 displayField : 'val',
23972                 editable : true,
23973                 listWidth : 100,
23974                 triggerAction : 'all',
23975                 typeAhead : true,
23976                 valueField : 'val',
23977                 width : 70,
23978                 name : 'align',
23979                 listeners : {
23980                     select : function (combo, r, index)
23981                     {
23982                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23983                         var b = block();
23984                         b.align = r.get('val');
23985                         b.updateElement();
23986                         syncValue();
23987                         toolbar.editorcore.onEditorEvent();
23988                     }
23989                 },
23990                 xns : rooui.form,
23991                 store : {
23992                     xtype : 'SimpleStore',
23993                     data : [
23994                         ['left'],
23995                         ['right'],
23996                         ['center']
23997                     ],
23998                     fields : [ 'val'],
23999                     xns : Roo.data
24000                 }
24001             },
24002             
24003               
24004             {
24005                 xtype : 'Button',
24006                 text: 'Hide Caption',
24007                 name : 'caption_display',
24008                 pressed : false,
24009                 enableToggle : true,
24010                 setValue : function(v) {
24011                     // this trigger toggle.
24012                      
24013                     this.setText(v ? "Hide Caption" : "Show Caption");
24014                     this.setPressed(v != 'block');
24015                 },
24016                 listeners : {
24017                     toggle: function (btn, state)
24018                     {
24019                         var b  = block();
24020                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
24021                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
24022                         b.updateElement();
24023                         syncValue();
24024                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24025                         toolbar.editorcore.onEditorEvent();
24026                     }
24027                 },
24028                 xns : rooui.Toolbar
24029             }
24030         ];
24031         
24032     },
24033     /**
24034      * create a DomHelper friendly object - for use with
24035      * Roo.DomHelper.markup / overwrite / etc..
24036      */
24037     toObject : function()
24038     {
24039         var d = document.createElement('div');
24040         d.innerHTML = this.caption;
24041         
24042         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
24043         
24044         var iw = this.align == 'center' ? this.width : '100%';
24045         var img =   {
24046             tag : 'img',
24047             contenteditable : 'false',
24048             src : this.image_src,
24049             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
24050             style: {
24051                 width : iw,
24052                 maxWidth : iw + ' !important', // this is not getting rendered?
24053                 margin : m  
24054                 
24055             }
24056         };
24057         /*
24058         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
24059                     '<a href="{2}">' + 
24060                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
24061                     '</a>' + 
24062                 '</div>',
24063         */
24064                 
24065         if (this.href.length > 0) {
24066             img = {
24067                 tag : 'a',
24068                 href: this.href,
24069                 contenteditable : 'true',
24070                 cn : [
24071                     img
24072                 ]
24073             };
24074         }
24075         
24076         
24077         if (this.video_url.length > 0) {
24078             img = {
24079                 tag : 'div',
24080                 cls : this.cls,
24081                 frameborder : 0,
24082                 allowfullscreen : true,
24083                 width : 420,  // these are for video tricks - that we replace the outer
24084                 height : 315,
24085                 src : this.video_url,
24086                 cn : [
24087                     img
24088                 ]
24089             };
24090         }
24091         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
24092         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
24093         
24094   
24095         var ret =   {
24096             tag: 'figure',
24097             'data-block' : 'Figure',
24098             'data-width' : this.width,
24099             'data-caption' : this.caption, 
24100             contenteditable : 'false',
24101             
24102             style : {
24103                 display: 'block',
24104                 float :  this.align ,
24105                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
24106                 width : this.align == 'center' ? '100%' : this.width,
24107                 margin:  '0px',
24108                 padding: this.align == 'center' ? '0' : '0 10px' ,
24109                 textAlign : this.align   // seems to work for email..
24110                 
24111             },
24112            
24113             
24114             align : this.align,
24115             cn : [
24116                 img,
24117               
24118                 {
24119                     tag: 'figcaption',
24120                     'data-display' : this.caption_display,
24121                     style : {
24122                         textAlign : 'left',
24123                         fontSize : '16px',
24124                         lineHeight : '24px',
24125                         display : this.caption_display,
24126                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
24127                         margin: m,
24128                         width: this.align == 'center' ?  this.width : '100%' 
24129                     
24130                          
24131                     },
24132                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
24133                     cn : [
24134                         {
24135                             tag: 'div',
24136                             style  : {
24137                                 marginTop : '16px',
24138                                 textAlign : 'left'
24139                             },
24140                             align: 'left',
24141                             cn : [
24142                                 {
24143                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
24144                                     tag : 'i',
24145                                     contenteditable : true,
24146                                     html : captionhtml
24147                                 }
24148                                 
24149                             ]
24150                         }
24151                         
24152                     ]
24153                     
24154                 }
24155             ]
24156         };
24157         return ret;
24158          
24159     },
24160     
24161     readElement : function(node)
24162     {
24163         // this should not really come from the link...
24164         this.video_url = this.getVal(node, 'div', 'src');
24165         this.cls = this.getVal(node, 'div', 'class');
24166         this.href = this.getVal(node, 'a', 'href');
24167         
24168         
24169         this.image_src = this.getVal(node, 'img', 'src');
24170          
24171         this.align = this.getVal(node, 'figure', 'align');
24172         
24173         /// not really used - as hidden captions do not store the content here..
24174         var figcaption = this.getVal(node, 'figcaption', false);
24175         if (figcaption !== '') {
24176             this.caption = this.getVal(figcaption, 'i', 'html');
24177         }
24178         
24179
24180         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
24181         var dc = this.getVal(node, true, 'data-caption');
24182         if (this.caption_display == 'none' && figcaption != '' && dc && dc.length) {
24183             this.caption = dc;
24184         }
24185         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
24186         this.width = this.getVal(node, true, 'data-width');
24187         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
24188         
24189     },
24190     removeNode : function()
24191     {
24192         return this.node;
24193     }
24194     
24195   
24196    
24197      
24198     
24199     
24200     
24201     
24202 })
24203
24204  
24205
24206 /**
24207  * @class Roo.htmleditor.BlockTable
24208  * Block that manages a table
24209  * 
24210  * @constructor
24211  * Create a new Filter.
24212  * @param {Object} config Configuration options
24213  */
24214
24215 Roo.htmleditor.BlockTable = function(cfg)
24216 {
24217     if (cfg.node) {
24218         this.readElement(cfg.node);
24219         this.updateElement(cfg.node);
24220     }
24221     Roo.apply(this, cfg);
24222     if (!cfg.node) {
24223         this.rows = [];
24224         for(var r = 0; r < this.no_row; r++) {
24225             this.rows[r] = [];
24226             for(var c = 0; c < this.no_col; c++) {
24227                 this.rows[r][c] = this.emptyCell();
24228             }
24229         }
24230     }
24231     
24232     
24233 }
24234 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
24235  
24236     rows : false,
24237     no_col : 1,
24238     no_row : 1,
24239     
24240     
24241     width: '100%',
24242     
24243     // used by context menu
24244     friendly_name : 'Table',
24245     deleteTitle : 'Delete Table',
24246     // context menu is drawn once..
24247     
24248     contextMenu : function(toolbar)
24249     {
24250         
24251         var block = function() {
24252             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24253         };
24254         
24255         
24256         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24257         
24258         var syncValue = toolbar.editorcore.syncValue;
24259         
24260         var fields = {};
24261         
24262         return [
24263             {
24264                 xtype : 'TextItem',
24265                 text : "Width: ",
24266                 xns : rooui.Toolbar  //Boostrap?
24267             },
24268             {
24269                 xtype : 'ComboBox',
24270                 allowBlank : false,
24271                 displayField : 'val',
24272                 editable : true,
24273                 listWidth : 100,
24274                 triggerAction : 'all',
24275                 typeAhead : true,
24276                 valueField : 'val',
24277                 width : 100,
24278                 name : 'width',
24279                 listeners : {
24280                     select : function (combo, r, index)
24281                     {
24282                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24283                         var b = block();
24284                         b.width = r.get('val');
24285                         b.updateElement();
24286                         syncValue();
24287                         toolbar.editorcore.onEditorEvent();
24288                     }
24289                 },
24290                 xns : rooui.form,
24291                 store : {
24292                     xtype : 'SimpleStore',
24293                     data : [
24294                         ['100%'],
24295                         ['auto']
24296                     ],
24297                     fields : [ 'val'],
24298                     xns : Roo.data
24299                 }
24300             },
24301             // -------- Cols
24302             
24303             {
24304                 xtype : 'TextItem',
24305                 text : "Columns: ",
24306                 xns : rooui.Toolbar  //Boostrap?
24307             },
24308          
24309             {
24310                 xtype : 'Button',
24311                 text: '-',
24312                 listeners : {
24313                     click : function (_self, e)
24314                     {
24315                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24316                         block().removeColumn();
24317                         syncValue();
24318                         toolbar.editorcore.onEditorEvent();
24319                     }
24320                 },
24321                 xns : rooui.Toolbar
24322             },
24323             {
24324                 xtype : 'Button',
24325                 text: '+',
24326                 listeners : {
24327                     click : function (_self, e)
24328                     {
24329                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24330                         block().addColumn();
24331                         syncValue();
24332                         toolbar.editorcore.onEditorEvent();
24333                     }
24334                 },
24335                 xns : rooui.Toolbar
24336             },
24337             // -------- ROWS
24338             {
24339                 xtype : 'TextItem',
24340                 text : "Rows: ",
24341                 xns : rooui.Toolbar  //Boostrap?
24342             },
24343          
24344             {
24345                 xtype : 'Button',
24346                 text: '-',
24347                 listeners : {
24348                     click : function (_self, e)
24349                     {
24350                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24351                         block().removeRow();
24352                         syncValue();
24353                         toolbar.editorcore.onEditorEvent();
24354                     }
24355                 },
24356                 xns : rooui.Toolbar
24357             },
24358             {
24359                 xtype : 'Button',
24360                 text: '+',
24361                 listeners : {
24362                     click : function (_self, e)
24363                     {
24364                         block().addRow();
24365                         syncValue();
24366                         toolbar.editorcore.onEditorEvent();
24367                     }
24368                 },
24369                 xns : rooui.Toolbar
24370             },
24371             // -------- ROWS
24372             {
24373                 xtype : 'Button',
24374                 text: 'Reset Column Widths',
24375                 listeners : {
24376                     
24377                     click : function (_self, e)
24378                     {
24379                         block().resetWidths();
24380                         syncValue();
24381                         toolbar.editorcore.onEditorEvent();
24382                     }
24383                 },
24384                 xns : rooui.Toolbar
24385             } 
24386             
24387             
24388             
24389         ];
24390         
24391     },
24392     
24393     
24394   /**
24395      * create a DomHelper friendly object - for use with
24396      * Roo.DomHelper.markup / overwrite / etc..
24397      * ?? should it be called with option to hide all editing features?
24398      */
24399     toObject : function()
24400     {
24401         
24402         var ret = {
24403             tag : 'table',
24404             contenteditable : 'false', // this stops cell selection from picking the table.
24405             'data-block' : 'Table',
24406             style : {
24407                 width:  this.width,
24408                 border : 'solid 1px #000', // ??? hard coded?
24409                 'border-collapse' : 'collapse' 
24410             },
24411             cn : [
24412                 { tag : 'tbody' , cn : [] }
24413             ]
24414         };
24415         
24416         // do we have a head = not really 
24417         var ncols = 0;
24418         Roo.each(this.rows, function( row ) {
24419             var tr = {
24420                 tag: 'tr',
24421                 style : {
24422                     margin: '6px',
24423                     border : 'solid 1px #000',
24424                     textAlign : 'left' 
24425                 },
24426                 cn : [ ]
24427             };
24428             
24429             ret.cn[0].cn.push(tr);
24430             // does the row have any properties? ?? height?
24431             var nc = 0;
24432             Roo.each(row, function( cell ) {
24433                 
24434                 var td = {
24435                     tag : 'td',
24436                     contenteditable :  'true',
24437                     'data-block' : 'Td',
24438                     html : cell.html,
24439                     style : cell.style
24440                 };
24441                 if (cell.colspan > 1) {
24442                     td.colspan = cell.colspan ;
24443                     nc += cell.colspan;
24444                 } else {
24445                     nc++;
24446                 }
24447                 if (cell.rowspan > 1) {
24448                     td.rowspan = cell.rowspan ;
24449                 }
24450                 
24451                 
24452                 // widths ?
24453                 tr.cn.push(td);
24454                     
24455                 
24456             }, this);
24457             ncols = Math.max(nc, ncols);
24458             
24459             
24460         }, this);
24461         // add the header row..
24462         
24463         ncols++;
24464          
24465         
24466         return ret;
24467          
24468     },
24469     
24470     readElement : function(node)
24471     {
24472         node  = node ? node : this.node ;
24473         this.width = this.getVal(node, true, 'style', 'width') || '100%';
24474         
24475         this.rows = [];
24476         this.no_row = 0;
24477         var trs = Array.from(node.rows);
24478         trs.forEach(function(tr) {
24479             var row =  [];
24480             this.rows.push(row);
24481             
24482             this.no_row++;
24483             var no_column = 0;
24484             Array.from(tr.cells).forEach(function(td) {
24485                 
24486                 var add = {
24487                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24488                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24489                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24490                     html : td.innerHTML
24491                 };
24492                 no_column += add.colspan;
24493                      
24494                 
24495                 row.push(add);
24496                 
24497                 
24498             },this);
24499             this.no_col = Math.max(this.no_col, no_column);
24500             
24501             
24502         },this);
24503         
24504         
24505     },
24506     normalizeRows: function()
24507     {
24508         var ret= [];
24509         var rid = -1;
24510         this.rows.forEach(function(row) {
24511             rid++;
24512             ret[rid] = [];
24513             row = this.normalizeRow(row);
24514             var cid = 0;
24515             row.forEach(function(c) {
24516                 while (typeof(ret[rid][cid]) != 'undefined') {
24517                     cid++;
24518                 }
24519                 if (typeof(ret[rid]) == 'undefined') {
24520                     ret[rid] = [];
24521                 }
24522                 ret[rid][cid] = c;
24523                 c.row = rid;
24524                 c.col = cid;
24525                 if (c.rowspan < 2) {
24526                     return;
24527                 }
24528                 
24529                 for(var i = 1 ;i < c.rowspan; i++) {
24530                     if (typeof(ret[rid+i]) == 'undefined') {
24531                         ret[rid+i] = [];
24532                     }
24533                     ret[rid+i][cid] = c;
24534                 }
24535             });
24536         }, this);
24537         return ret;
24538     
24539     },
24540     
24541     normalizeRow: function(row)
24542     {
24543         var ret= [];
24544         row.forEach(function(c) {
24545             if (c.colspan < 2) {
24546                 ret.push(c);
24547                 return;
24548             }
24549             for(var i =0 ;i < c.colspan; i++) {
24550                 ret.push(c);
24551             }
24552         });
24553         return ret;
24554     
24555     },
24556     
24557     deleteColumn : function(sel)
24558     {
24559         if (!sel || sel.type != 'col') {
24560             return;
24561         }
24562         if (this.no_col < 2) {
24563             return;
24564         }
24565         
24566         this.rows.forEach(function(row) {
24567             var cols = this.normalizeRow(row);
24568             var col = cols[sel.col];
24569             if (col.colspan > 1) {
24570                 col.colspan --;
24571             } else {
24572                 row.remove(col);
24573             }
24574             
24575         }, this);
24576         this.no_col--;
24577         
24578     },
24579     removeColumn : function()
24580     {
24581         this.deleteColumn({
24582             type: 'col',
24583             col : this.no_col-1
24584         });
24585         this.updateElement();
24586     },
24587     
24588      
24589     addColumn : function()
24590     {
24591         
24592         this.rows.forEach(function(row) {
24593             row.push(this.emptyCell());
24594            
24595         }, this);
24596         this.updateElement();
24597     },
24598     
24599     deleteRow : function(sel)
24600     {
24601         if (!sel || sel.type != 'row') {
24602             return;
24603         }
24604         
24605         if (this.no_row < 2) {
24606             return;
24607         }
24608         
24609         var rows = this.normalizeRows();
24610         
24611         
24612         rows[sel.row].forEach(function(col) {
24613             if (col.rowspan > 1) {
24614                 col.rowspan--;
24615             } else {
24616                 col.remove = 1; // flage it as removed.
24617             }
24618             
24619         }, this);
24620         var newrows = [];
24621         this.rows.forEach(function(row) {
24622             newrow = [];
24623             row.forEach(function(c) {
24624                 if (typeof(c.remove) == 'undefined') {
24625                     newrow.push(c);
24626                 }
24627                 
24628             });
24629             if (newrow.length > 0) {
24630                 newrows.push(row);
24631             }
24632         });
24633         this.rows =  newrows;
24634         
24635         
24636         
24637         this.no_row--;
24638         this.updateElement();
24639         
24640     },
24641     removeRow : function()
24642     {
24643         this.deleteRow({
24644             type: 'row',
24645             row : this.no_row-1
24646         });
24647         
24648     },
24649     
24650      
24651     addRow : function()
24652     {
24653         
24654         var row = [];
24655         for (var i = 0; i < this.no_col; i++ ) {
24656             
24657             row.push(this.emptyCell());
24658            
24659         }
24660         this.rows.push(row);
24661         this.updateElement();
24662         
24663     },
24664      
24665     // the default cell object... at present...
24666     emptyCell : function() {
24667         return (new Roo.htmleditor.BlockTd({})).toObject();
24668         
24669      
24670     },
24671     
24672     removeNode : function()
24673     {
24674         return this.node;
24675     },
24676     
24677     
24678     
24679     resetWidths : function()
24680     {
24681         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24682             var nn = Roo.htmleditor.Block.factory(n);
24683             nn.width = '';
24684             nn.updateElement(n);
24685         });
24686     }
24687     
24688     
24689     
24690     
24691 })
24692
24693 /**
24694  *
24695  * editing a TD?
24696  *
24697  * since selections really work on the table cell, then editing really should work from there
24698  *
24699  * The original plan was to support merging etc... - but that may not be needed yet..
24700  *
24701  * So this simple version will support:
24702  *   add/remove cols
24703  *   adjust the width +/-
24704  *   reset the width...
24705  *   
24706  *
24707  */
24708
24709
24710  
24711
24712 /**
24713  * @class Roo.htmleditor.BlockTable
24714  * Block that manages a table
24715  * 
24716  * @constructor
24717  * Create a new Filter.
24718  * @param {Object} config Configuration options
24719  */
24720
24721 Roo.htmleditor.BlockTd = function(cfg)
24722 {
24723     if (cfg.node) {
24724         this.readElement(cfg.node);
24725         this.updateElement(cfg.node);
24726     }
24727     Roo.apply(this, cfg);
24728      
24729     
24730     
24731 }
24732 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24733  
24734     node : false,
24735     
24736     width: '',
24737     textAlign : 'left',
24738     valign : 'top',
24739     
24740     colspan : 1,
24741     rowspan : 1,
24742     
24743     
24744     // used by context menu
24745     friendly_name : 'Table Cell',
24746     deleteTitle : false, // use our customer delete
24747     
24748     // context menu is drawn once..
24749     
24750     contextMenu : function(toolbar)
24751     {
24752         
24753         var cell = function() {
24754             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24755         };
24756         
24757         var table = function() {
24758             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24759         };
24760         
24761         var lr = false;
24762         var saveSel = function()
24763         {
24764             lr = toolbar.editorcore.getSelection().getRangeAt(0);
24765         }
24766         var restoreSel = function()
24767         {
24768             if (lr) {
24769                 (function() {
24770                     toolbar.editorcore.focus();
24771                     var cr = toolbar.editorcore.getSelection();
24772                     cr.removeAllRanges();
24773                     cr.addRange(lr);
24774                     toolbar.editorcore.onEditorEvent();
24775                 }).defer(10, this);
24776                 
24777                 
24778             }
24779         }
24780         
24781         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24782         
24783         var syncValue = toolbar.editorcore.syncValue;
24784         
24785         var fields = {};
24786         
24787         return [
24788             {
24789                 xtype : 'Button',
24790                 text : 'Edit Table',
24791                 listeners : {
24792                     click : function() {
24793                         var t = toolbar.tb.selectedNode.closest('table');
24794                         toolbar.editorcore.selectNode(t);
24795                         toolbar.editorcore.onEditorEvent();                        
24796                     }
24797                 }
24798                 
24799             },
24800               
24801            
24802              
24803             {
24804                 xtype : 'TextItem',
24805                 text : "Column Width: ",
24806                  xns : rooui.Toolbar 
24807                
24808             },
24809             {
24810                 xtype : 'Button',
24811                 text: '-',
24812                 listeners : {
24813                     click : function (_self, e)
24814                     {
24815                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24816                         cell().shrinkColumn();
24817                         syncValue();
24818                          toolbar.editorcore.onEditorEvent();
24819                     }
24820                 },
24821                 xns : rooui.Toolbar
24822             },
24823             {
24824                 xtype : 'Button',
24825                 text: '+',
24826                 listeners : {
24827                     click : function (_self, e)
24828                     {
24829                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24830                         cell().growColumn();
24831                         syncValue();
24832                         toolbar.editorcore.onEditorEvent();
24833                     }
24834                 },
24835                 xns : rooui.Toolbar
24836             },
24837             
24838             {
24839                 xtype : 'TextItem',
24840                 text : "Vertical Align: ",
24841                 xns : rooui.Toolbar  //Boostrap?
24842             },
24843             {
24844                 xtype : 'ComboBox',
24845                 allowBlank : false,
24846                 displayField : 'val',
24847                 editable : true,
24848                 listWidth : 100,
24849                 triggerAction : 'all',
24850                 typeAhead : true,
24851                 valueField : 'val',
24852                 width : 100,
24853                 name : 'valign',
24854                 listeners : {
24855                     select : function (combo, r, index)
24856                     {
24857                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24858                         var b = cell();
24859                         b.valign = r.get('val');
24860                         b.updateElement();
24861                         syncValue();
24862                         toolbar.editorcore.onEditorEvent();
24863                     }
24864                 },
24865                 xns : rooui.form,
24866                 store : {
24867                     xtype : 'SimpleStore',
24868                     data : [
24869                         ['top'],
24870                         ['middle'],
24871                         ['bottom'] // there are afew more... 
24872                     ],
24873                     fields : [ 'val'],
24874                     xns : Roo.data
24875                 }
24876             },
24877             
24878             {
24879                 xtype : 'TextItem',
24880                 text : "Merge Cells: ",
24881                  xns : rooui.Toolbar 
24882                
24883             },
24884             
24885             
24886             {
24887                 xtype : 'Button',
24888                 text: 'Right',
24889                 listeners : {
24890                     click : function (_self, e)
24891                     {
24892                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24893                         cell().mergeRight();
24894                         //block().growColumn();
24895                         syncValue();
24896                         toolbar.editorcore.onEditorEvent();
24897                     }
24898                 },
24899                 xns : rooui.Toolbar
24900             },
24901              
24902             {
24903                 xtype : 'Button',
24904                 text: 'Below',
24905                 listeners : {
24906                     click : function (_self, e)
24907                     {
24908                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24909                         cell().mergeBelow();
24910                         //block().growColumn();
24911                         syncValue();
24912                         toolbar.editorcore.onEditorEvent();
24913                     }
24914                 },
24915                 xns : rooui.Toolbar
24916             },
24917             {
24918                 xtype : 'TextItem',
24919                 text : "| ",
24920                  xns : rooui.Toolbar 
24921                
24922             },
24923             
24924             {
24925                 xtype : 'Button',
24926                 text: 'Split',
24927                 listeners : {
24928                     click : function (_self, e)
24929                     {
24930                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24931                         cell().split();
24932                         syncValue();
24933                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24934                         toolbar.editorcore.onEditorEvent();
24935                                              
24936                     }
24937                 },
24938                 xns : rooui.Toolbar
24939             },
24940             {
24941                 xtype : 'Fill',
24942                 xns : rooui.Toolbar 
24943                
24944             },
24945         
24946           
24947             {
24948                 xtype : 'Button',
24949                 text: 'Delete',
24950                  
24951                 xns : rooui.Toolbar,
24952                 menu : {
24953                     xtype : 'Menu',
24954                     xns : rooui.menu,
24955                     items : [
24956                         {
24957                             xtype : 'Item',
24958                             html: 'Column',
24959                             listeners : {
24960                                 click : function (_self, e)
24961                                 {
24962                                     var t = table();
24963                                     
24964                                     cell().deleteColumn();
24965                                     syncValue();
24966                                     toolbar.editorcore.selectNode(t.node);
24967                                     toolbar.editorcore.onEditorEvent();   
24968                                 }
24969                             },
24970                             xns : rooui.menu
24971                         },
24972                         {
24973                             xtype : 'Item',
24974                             html: 'Row',
24975                             listeners : {
24976                                 click : function (_self, e)
24977                                 {
24978                                     var t = table();
24979                                     cell().deleteRow();
24980                                     syncValue();
24981                                     
24982                                     toolbar.editorcore.selectNode(t.node);
24983                                     toolbar.editorcore.onEditorEvent();   
24984                                                          
24985                                 }
24986                             },
24987                             xns : rooui.menu
24988                         },
24989                        {
24990                             xtype : 'Separator',
24991                             xns : rooui.menu
24992                         },
24993                         {
24994                             xtype : 'Item',
24995                             html: 'Table',
24996                             listeners : {
24997                                 click : function (_self, e)
24998                                 {
24999                                     var t = table();
25000                                     var nn = t.node.nextSibling || t.node.previousSibling;
25001                                     t.node.parentNode.removeChild(t.node);
25002                                     if (nn) { 
25003                                         toolbar.editorcore.selectNode(nn, true);
25004                                     }
25005                                     toolbar.editorcore.onEditorEvent();   
25006                                                          
25007                                 }
25008                             },
25009                             xns : rooui.menu
25010                         }
25011                     ]
25012                 }
25013             }
25014             
25015             // align... << fixme
25016             
25017         ];
25018         
25019     },
25020     
25021     
25022   /**
25023      * create a DomHelper friendly object - for use with
25024      * Roo.DomHelper.markup / overwrite / etc..
25025      * ?? should it be called with option to hide all editing features?
25026      */
25027  /**
25028      * create a DomHelper friendly object - for use with
25029      * Roo.DomHelper.markup / overwrite / etc..
25030      * ?? should it be called with option to hide all editing features?
25031      */
25032     toObject : function()
25033     {
25034         var ret = {
25035             tag : 'td',
25036             contenteditable : 'true', // this stops cell selection from picking the table.
25037             'data-block' : 'Td',
25038             valign : this.valign,
25039             style : {  
25040                 'text-align' :  this.textAlign,
25041                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
25042                 'border-collapse' : 'collapse',
25043                 padding : '6px', // 8 for desktop / 4 for mobile
25044                 'vertical-align': this.valign
25045             },
25046             html : this.html
25047         };
25048         if (this.width != '') {
25049             ret.width = this.width;
25050             ret.style.width = this.width;
25051         }
25052         
25053         
25054         if (this.colspan > 1) {
25055             ret.colspan = this.colspan ;
25056         } 
25057         if (this.rowspan > 1) {
25058             ret.rowspan = this.rowspan ;
25059         }
25060         
25061            
25062         
25063         return ret;
25064          
25065     },
25066     
25067     readElement : function(node)
25068     {
25069         node  = node ? node : this.node ;
25070         this.width = node.style.width;
25071         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
25072         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
25073         this.html = node.innerHTML;
25074         if (node.style.textAlign != '') {
25075             this.textAlign = node.style.textAlign;
25076         }
25077         
25078         
25079     },
25080      
25081     // the default cell object... at present...
25082     emptyCell : function() {
25083         return {
25084             colspan :  1,
25085             rowspan :  1,
25086             textAlign : 'left',
25087             html : "&nbsp;" // is this going to be editable now?
25088         };
25089      
25090     },
25091     
25092     removeNode : function()
25093     {
25094         return this.node.closest('table');
25095          
25096     },
25097     
25098     cellData : false,
25099     
25100     colWidths : false,
25101     
25102     toTableArray  : function()
25103     {
25104         var ret = [];
25105         var tab = this.node.closest('tr').closest('table');
25106         Array.from(tab.rows).forEach(function(r, ri){
25107             ret[ri] = [];
25108         });
25109         var rn = 0;
25110         this.colWidths = [];
25111         var all_auto = true;
25112         Array.from(tab.rows).forEach(function(r, ri){
25113             
25114             var cn = 0;
25115             Array.from(r.cells).forEach(function(ce, ci){
25116                 var c =  {
25117                     cell : ce,
25118                     row : rn,
25119                     col: cn,
25120                     colspan : ce.colSpan,
25121                     rowspan : ce.rowSpan
25122                 };
25123                 if (ce.isEqualNode(this.node)) {
25124                     this.cellData = c;
25125                 }
25126                 // if we have been filled up by a row?
25127                 if (typeof(ret[rn][cn]) != 'undefined') {
25128                     while(typeof(ret[rn][cn]) != 'undefined') {
25129                         cn++;
25130                     }
25131                     c.col = cn;
25132                 }
25133                 
25134                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
25135                     this.colWidths[cn] =   ce.style.width;
25136                     if (this.colWidths[cn] != '') {
25137                         all_auto = false;
25138                     }
25139                 }
25140                 
25141                 
25142                 if (c.colspan < 2 && c.rowspan < 2 ) {
25143                     ret[rn][cn] = c;
25144                     cn++;
25145                     return;
25146                 }
25147                 for(var j = 0; j < c.rowspan; j++) {
25148                     if (typeof(ret[rn+j]) == 'undefined') {
25149                         continue; // we have a problem..
25150                     }
25151                     ret[rn+j][cn] = c;
25152                     for(var i = 0; i < c.colspan; i++) {
25153                         ret[rn+j][cn+i] = c;
25154                     }
25155                 }
25156                 
25157                 cn += c.colspan;
25158             }, this);
25159             rn++;
25160         }, this);
25161         
25162         // initalize widths.?
25163         // either all widths or no widths..
25164         if (all_auto) {
25165             this.colWidths[0] = false; // no widths flag.
25166         }
25167         
25168         
25169         return ret;
25170         
25171     },
25172     
25173     
25174     
25175     
25176     mergeRight: function()
25177     {
25178          
25179         // get the contents of the next cell along..
25180         var tr = this.node.closest('tr');
25181         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
25182         if (i >= tr.childNodes.length - 1) {
25183             return; // no cells on right to merge with.
25184         }
25185         var table = this.toTableArray();
25186         
25187         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
25188             return; // nothing right?
25189         }
25190         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
25191         // right cell - must be same rowspan and on the same row.
25192         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
25193             return; // right hand side is not same rowspan.
25194         }
25195         
25196         
25197         
25198         this.node.innerHTML += ' ' + rc.cell.innerHTML;
25199         tr.removeChild(rc.cell);
25200         this.colspan += rc.colspan;
25201         this.node.setAttribute('colspan', this.colspan);
25202
25203         var table = this.toTableArray();
25204         this.normalizeWidths(table);
25205         this.updateWidths(table);
25206     },
25207     
25208     
25209     mergeBelow : function()
25210     {
25211         var table = this.toTableArray();
25212         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
25213             return; // no row below
25214         }
25215         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
25216             return; // nothing right?
25217         }
25218         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
25219         
25220         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
25221             return; // right hand side is not same rowspan.
25222         }
25223         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
25224         rc.cell.parentNode.removeChild(rc.cell);
25225         this.rowspan += rc.rowspan;
25226         this.node.setAttribute('rowspan', this.rowspan);
25227     },
25228     
25229     split: function()
25230     {
25231         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
25232             return;
25233         }
25234         var table = this.toTableArray();
25235         var cd = this.cellData;
25236         this.rowspan = 1;
25237         this.colspan = 1;
25238         
25239         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
25240              
25241             
25242             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
25243                 if (r == cd.row && c == cd.col) {
25244                     this.node.removeAttribute('rowspan');
25245                     this.node.removeAttribute('colspan');
25246                 }
25247                  
25248                 var ntd = this.node.cloneNode(); // which col/row should be 0..
25249                 ntd.removeAttribute('id'); 
25250                 ntd.style.width  = this.colWidths[c];
25251                 ntd.innerHTML = '';
25252                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
25253             }
25254             
25255         }
25256         this.redrawAllCells(table);
25257         
25258     },
25259     
25260     
25261     
25262     redrawAllCells: function(table)
25263     {
25264         
25265          
25266         var tab = this.node.closest('tr').closest('table');
25267         var ctr = tab.rows[0].parentNode;
25268         Array.from(tab.rows).forEach(function(r, ri){
25269             
25270             Array.from(r.cells).forEach(function(ce, ci){
25271                 ce.parentNode.removeChild(ce);
25272             });
25273             r.parentNode.removeChild(r);
25274         });
25275         for(var r = 0 ; r < table.length; r++) {
25276             var re = tab.rows[r];
25277             
25278             var re = tab.ownerDocument.createElement('tr');
25279             ctr.appendChild(re);
25280             for(var c = 0 ; c < table[r].length; c++) {
25281                 if (table[r][c].cell === false) {
25282                     continue;
25283                 }
25284                 
25285                 re.appendChild(table[r][c].cell);
25286                  
25287                 table[r][c].cell = false;
25288             }
25289         }
25290         
25291     },
25292     updateWidths : function(table)
25293     {
25294         for(var r = 0 ; r < table.length; r++) {
25295            
25296             for(var c = 0 ; c < table[r].length; c++) {
25297                 if (table[r][c].cell === false) {
25298                     continue;
25299                 }
25300                 
25301                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
25302                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
25303                     el.width = Math.floor(this.colWidths[c])  +'%';
25304                     el.updateElement(el.node);
25305                 }
25306                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
25307                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
25308                     var width = 0;
25309                     for(var i = 0; i < table[r][c].colspan; i ++) {
25310                         width += Math.floor(this.colWidths[c + i]);
25311                     }
25312                     el.width = width  +'%';
25313                     el.updateElement(el.node);
25314                 }
25315                 table[r][c].cell = false; // done
25316             }
25317         }
25318     },
25319     normalizeWidths : function(table)
25320     {
25321         if (this.colWidths[0] === false) {
25322             var nw = 100.0 / this.colWidths.length;
25323             this.colWidths.forEach(function(w,i) {
25324                 this.colWidths[i] = nw;
25325             },this);
25326             return;
25327         }
25328     
25329         var t = 0, missing = [];
25330         
25331         this.colWidths.forEach(function(w,i) {
25332             //if you mix % and
25333             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
25334             var add =  this.colWidths[i];
25335             if (add > 0) {
25336                 t+=add;
25337                 return;
25338             }
25339             missing.push(i);
25340             
25341             
25342         },this);
25343         var nc = this.colWidths.length;
25344         if (missing.length) {
25345             var mult = (nc - missing.length) / (1.0 * nc);
25346             var t = mult * t;
25347             var ew = (100 -t) / (1.0 * missing.length);
25348             this.colWidths.forEach(function(w,i) {
25349                 if (w > 0) {
25350                     this.colWidths[i] = w * mult;
25351                     return;
25352                 }
25353                 
25354                 this.colWidths[i] = ew;
25355             }, this);
25356             // have to make up numbers..
25357              
25358         }
25359         // now we should have all the widths..
25360         
25361     
25362     },
25363     
25364     shrinkColumn : function()
25365     {
25366         var table = this.toTableArray();
25367         this.normalizeWidths(table);
25368         var col = this.cellData.col;
25369         var nw = this.colWidths[col] * 0.8;
25370         if (nw < 5) {
25371             return;
25372         }
25373         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
25374         this.colWidths.forEach(function(w,i) {
25375             if (i == col) {
25376                  this.colWidths[i] = nw;
25377                 return;
25378             }
25379             this.colWidths[i] += otherAdd
25380         }, this);
25381         this.updateWidths(table);
25382          
25383     },
25384     growColumn : function()
25385     {
25386         var table = this.toTableArray();
25387         this.normalizeWidths(table);
25388         var col = this.cellData.col;
25389         var nw = this.colWidths[col] * 1.2;
25390         if (nw > 90) {
25391             return;
25392         }
25393         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
25394         this.colWidths.forEach(function(w,i) {
25395             if (i == col) {
25396                 this.colWidths[i] = nw;
25397                 return;
25398             }
25399             this.colWidths[i] -= otherSub
25400         }, this);
25401         this.updateWidths(table);
25402          
25403     },
25404     deleteRow : function()
25405     {
25406         // delete this rows 'tr'
25407         // if any of the cells in this row have a rowspan > 1 && row!= this row..
25408         // then reduce the rowspan.
25409         var table = this.toTableArray();
25410         // this.cellData.row;
25411         for (var i =0;i< table[this.cellData.row].length ; i++) {
25412             var c = table[this.cellData.row][i];
25413             if (c.row != this.cellData.row) {
25414                 
25415                 c.rowspan--;
25416                 c.cell.setAttribute('rowspan', c.rowspan);
25417                 continue;
25418             }
25419             if (c.rowspan > 1) {
25420                 c.rowspan--;
25421                 c.cell.setAttribute('rowspan', c.rowspan);
25422             }
25423         }
25424         table.splice(this.cellData.row,1);
25425         this.redrawAllCells(table);
25426         
25427     },
25428     deleteColumn : function()
25429     {
25430         var table = this.toTableArray();
25431         
25432         for (var i =0;i< table.length ; i++) {
25433             var c = table[i][this.cellData.col];
25434             if (c.col != this.cellData.col) {
25435                 table[i][this.cellData.col].colspan--;
25436             } else if (c.colspan > 1) {
25437                 c.colspan--;
25438                 c.cell.setAttribute('colspan', c.colspan);
25439             }
25440             table[i].splice(this.cellData.col,1);
25441         }
25442         
25443         this.redrawAllCells(table);
25444     }
25445     
25446     
25447     
25448     
25449 })
25450
25451 //<script type="text/javascript">
25452
25453 /*
25454  * Based  Ext JS Library 1.1.1
25455  * Copyright(c) 2006-2007, Ext JS, LLC.
25456  * LGPL
25457  *
25458  */
25459  
25460 /**
25461  * @class Roo.HtmlEditorCore
25462  * @extends Roo.Component
25463  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25464  *
25465  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25466  */
25467
25468 Roo.HtmlEditorCore = function(config){
25469     
25470     
25471     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25472     
25473     
25474     this.addEvents({
25475         /**
25476          * @event initialize
25477          * Fires when the editor is fully initialized (including the iframe)
25478          * @param {Roo.HtmlEditorCore} this
25479          */
25480         initialize: true,
25481         /**
25482          * @event activate
25483          * Fires when the editor is first receives the focus. Any insertion must wait
25484          * until after this event.
25485          * @param {Roo.HtmlEditorCore} this
25486          */
25487         activate: true,
25488          /**
25489          * @event beforesync
25490          * Fires before the textarea is updated with content from the editor iframe. Return false
25491          * to cancel the sync.
25492          * @param {Roo.HtmlEditorCore} this
25493          * @param {String} html
25494          */
25495         beforesync: true,
25496          /**
25497          * @event beforepush
25498          * Fires before the iframe editor is updated with content from the textarea. Return false
25499          * to cancel the push.
25500          * @param {Roo.HtmlEditorCore} this
25501          * @param {String} html
25502          */
25503         beforepush: true,
25504          /**
25505          * @event sync
25506          * Fires when the textarea is updated with content from the editor iframe.
25507          * @param {Roo.HtmlEditorCore} this
25508          * @param {String} html
25509          */
25510         sync: true,
25511          /**
25512          * @event push
25513          * Fires when the iframe editor is updated with content from the textarea.
25514          * @param {Roo.HtmlEditorCore} this
25515          * @param {String} html
25516          */
25517         push: true,
25518         
25519         /**
25520          * @event editorevent
25521          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25522          * @param {Roo.HtmlEditorCore} this
25523          */
25524         editorevent: true 
25525         
25526         
25527     });
25528     
25529     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25530     
25531     // defaults : white / black...
25532     this.applyBlacklists();
25533     
25534     
25535     
25536 };
25537
25538
25539 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25540
25541
25542      /**
25543      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25544      */
25545     
25546     owner : false,
25547     
25548      /**
25549      * @cfg {String} css styling for resizing. (used on bootstrap only)
25550      */
25551     resize : false,
25552      /**
25553      * @cfg {Number} height (in pixels)
25554      */   
25555     height: 300,
25556    /**
25557      * @cfg {Number} width (in pixels)
25558      */   
25559     width: 500,
25560      /**
25561      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25562      *         if you are doing an email editor, this probably needs disabling, it's designed
25563      */
25564     autoClean: true,
25565     
25566     /**
25567      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25568      */
25569     enableBlocks : true,
25570     /**
25571      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25572      * 
25573      */
25574     stylesheets: false,
25575      /**
25576      * @cfg {String} language default en - language of text (usefull for rtl languages)
25577      * 
25578      */
25579     language: 'en',
25580     
25581     /**
25582      * @cfg {boolean} allowComments - default false - allow comments in HTML source
25583      *          - by default they are stripped - if you are editing email you may need this.
25584      */
25585     allowComments: false,
25586     // id of frame..
25587     frameId: false,
25588     
25589     // private properties
25590     validationEvent : false,
25591     deferHeight: true,
25592     initialized : false,
25593     activated : false,
25594     sourceEditMode : false,
25595     onFocus : Roo.emptyFn,
25596     iframePad:3,
25597     hideMode:'offsets',
25598     
25599     clearUp: true,
25600     
25601     // blacklist + whitelisted elements..
25602     black: false,
25603     white: false,
25604      
25605     bodyCls : '',
25606
25607     
25608     undoManager : false,
25609     /**
25610      * Protected method that will not generally be called directly. It
25611      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25612      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25613      */
25614     getDocMarkup : function(){
25615         // body styles..
25616         var st = '';
25617         
25618         // inherit styels from page...?? 
25619         if (this.stylesheets === false) {
25620             
25621             Roo.get(document.head).select('style').each(function(node) {
25622                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25623             });
25624             
25625             Roo.get(document.head).select('link').each(function(node) { 
25626                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25627             });
25628             
25629         } else if (!this.stylesheets.length) {
25630                 // simple..
25631                 st = '<style type="text/css">' +
25632                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25633                    '</style>';
25634         } else {
25635             for (var i in this.stylesheets) {
25636                 if (typeof(this.stylesheets[i]) != 'string') {
25637                     continue;
25638                 }
25639                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25640             }
25641             
25642         }
25643         
25644         st +=  '<style type="text/css">' +
25645             'IMG { cursor: pointer } ' +
25646         '</style>';
25647         
25648         st += '<meta name="google" content="notranslate">';
25649         
25650         var cls = 'notranslate roo-htmleditor-body';
25651         
25652         if(this.bodyCls.length){
25653             cls += ' ' + this.bodyCls;
25654         }
25655         
25656         return '<html  class="notranslate" translate="no"><head>' + st  +
25657             //<style type="text/css">' +
25658             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25659             //'</style>' +
25660             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25661     },
25662
25663     // private
25664     onRender : function(ct, position)
25665     {
25666         var _t = this;
25667         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25668         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25669         
25670         
25671         this.el.dom.style.border = '0 none';
25672         this.el.dom.setAttribute('tabIndex', -1);
25673         this.el.addClass('x-hidden hide');
25674         
25675         
25676         
25677         if(Roo.isIE){ // fix IE 1px bogus margin
25678             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25679         }
25680        
25681         
25682         this.frameId = Roo.id();
25683         
25684         var ifcfg = {
25685             tag: 'iframe',
25686             cls: 'form-control', // bootstrap..
25687             id: this.frameId,
25688             name: this.frameId,
25689             frameBorder : 'no',
25690             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25691         };
25692         if (this.resize) {
25693             ifcfg.style = { resize : this.resize };
25694         }
25695         
25696         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
25697         
25698         
25699         this.iframe = iframe.dom;
25700
25701         this.assignDocWin();
25702         
25703         this.doc.designMode = 'on';
25704        
25705         this.doc.open();
25706         this.doc.write(this.getDocMarkup());
25707         this.doc.close();
25708
25709         
25710         var task = { // must defer to wait for browser to be ready
25711             run : function(){
25712                 //console.log("run task?" + this.doc.readyState);
25713                 this.assignDocWin();
25714                 if(this.doc.body || this.doc.readyState == 'complete'){
25715                     try {
25716                         this.doc.designMode="on";
25717                         
25718                     } catch (e) {
25719                         return;
25720                     }
25721                     Roo.TaskMgr.stop(task);
25722                     this.initEditor.defer(10, this);
25723                 }
25724             },
25725             interval : 10,
25726             duration: 10000,
25727             scope: this
25728         };
25729         Roo.TaskMgr.start(task);
25730
25731     },
25732
25733     // private
25734     onResize : function(w, h)
25735     {
25736          Roo.log('resize: ' +w + ',' + h );
25737         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25738         if(!this.iframe){
25739             return;
25740         }
25741         if(typeof w == 'number'){
25742             
25743             this.iframe.style.width = w + 'px';
25744         }
25745         if(typeof h == 'number'){
25746             
25747             this.iframe.style.height = h + 'px';
25748             if(this.doc){
25749                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25750             }
25751         }
25752         
25753     },
25754
25755     /**
25756      * Toggles the editor between standard and source edit mode.
25757      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25758      */
25759     toggleSourceEdit : function(sourceEditMode){
25760         
25761         this.sourceEditMode = sourceEditMode === true;
25762         
25763         if(this.sourceEditMode){
25764  
25765             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
25766             
25767         }else{
25768             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25769             //this.iframe.className = '';
25770             this.deferFocus();
25771         }
25772         //this.setSize(this.owner.wrap.getSize());
25773         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25774     },
25775
25776     
25777   
25778
25779     /**
25780      * Protected method that will not generally be called directly. If you need/want
25781      * custom HTML cleanup, this is the method you should override.
25782      * @param {String} html The HTML to be cleaned
25783      * return {String} The cleaned HTML
25784      */
25785     cleanHtml : function(html)
25786     {
25787         html = String(html);
25788         if(html.length > 5){
25789             if(Roo.isSafari){ // strip safari nonsense
25790                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25791             }
25792         }
25793         if(html == '&nbsp;'){
25794             html = '';
25795         }
25796         return html;
25797     },
25798
25799     /**
25800      * HTML Editor -> Textarea
25801      * Protected method that will not generally be called directly. Syncs the contents
25802      * of the editor iframe with the textarea.
25803      */
25804     syncValue : function()
25805     {
25806         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25807         if(this.initialized){
25808             
25809             if (this.undoManager) {
25810                 this.undoManager.addEvent();
25811             }
25812
25813             
25814             var bd = (this.doc.body || this.doc.documentElement);
25815            
25816             
25817             var sel = this.win.getSelection();
25818             
25819             var div = document.createElement('div');
25820             div.innerHTML = bd.innerHTML;
25821             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25822             if (gtx.length > 0) {
25823                 var rm = gtx.item(0).parentNode;
25824                 rm.parentNode.removeChild(rm);
25825             }
25826             
25827            
25828             if (this.enableBlocks) {
25829                 new Roo.htmleditor.FilterBlock({ node : div });
25830             }
25831             
25832             var html = div.innerHTML;
25833             
25834             //?? tidy?
25835             if (this.autoClean) {
25836                 
25837                 new Roo.htmleditor.FilterAttributes({
25838                     node : div,
25839                     attrib_white : [
25840                             'href',
25841                             'src',
25842                             'name',
25843                             'align',
25844                             'colspan',
25845                             'rowspan',
25846                             'data-display',
25847                             'data-width',
25848                             'data-caption',
25849                             'start' ,
25850                             'style',
25851                             // youtube embed.
25852                             'class',
25853                             'allowfullscreen',
25854                             'frameborder',
25855                             'width',
25856                             'height',
25857                             'alt'
25858                             ],
25859                     attrib_clean : ['href', 'src' ] 
25860                 });
25861                 
25862                 var tidy = new Roo.htmleditor.TidySerializer({
25863                     inner:  true
25864                 });
25865                 html  = tidy.serialize(div);
25866                 
25867             }
25868             
25869             
25870             if(Roo.isSafari){
25871                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25872                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25873                 if(m && m[1]){
25874                     html = '<div style="'+m[0]+'">' + html + '</div>';
25875                 }
25876             }
25877             html = this.cleanHtml(html);
25878             // fix up the special chars.. normaly like back quotes in word...
25879             // however we do not want to do this with chinese..
25880             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25881                 
25882                 var cc = match.charCodeAt();
25883
25884                 // Get the character value, handling surrogate pairs
25885                 if (match.length == 2) {
25886                     // It's a surrogate pair, calculate the Unicode code point
25887                     var high = match.charCodeAt(0) - 0xD800;
25888                     var low  = match.charCodeAt(1) - 0xDC00;
25889                     cc = (high * 0x400) + low + 0x10000;
25890                 }  else if (
25891                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25892                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25893                     (cc >= 0xf900 && cc < 0xfb00 )
25894                 ) {
25895                         return match;
25896                 }  
25897          
25898                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25899                 return "&#" + cc + ";";
25900                 
25901                 
25902             });
25903             
25904             
25905              
25906             if(this.owner.fireEvent('beforesync', this, html) !== false){
25907                 this.el.dom.value = html;
25908                 this.owner.fireEvent('sync', this, html);
25909             }
25910         }
25911     },
25912
25913     /**
25914      * TEXTAREA -> EDITABLE
25915      * Protected method that will not generally be called directly. Pushes the value of the textarea
25916      * into the iframe editor.
25917      */
25918     pushValue : function()
25919     {
25920         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25921         if(this.initialized){
25922             var v = this.el.dom.value.trim();
25923             
25924             
25925             if(this.owner.fireEvent('beforepush', this, v) !== false){
25926                 var d = (this.doc.body || this.doc.documentElement);
25927                 d.innerHTML = v;
25928                  
25929                 this.el.dom.value = d.innerHTML;
25930                 this.owner.fireEvent('push', this, v);
25931             }
25932             if (this.autoClean) {
25933                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25934                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25935             }
25936             if (this.enableBlocks) {
25937                 Roo.htmleditor.Block.initAll(this.doc.body);
25938             }
25939             
25940             this.updateLanguage();
25941             
25942             var lc = this.doc.body.lastChild;
25943             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25944                 // add an extra line at the end.
25945                 this.doc.body.appendChild(this.doc.createElement('br'));
25946             }
25947             
25948             
25949         }
25950     },
25951
25952     // private
25953     deferFocus : function(){
25954         this.focus.defer(10, this);
25955     },
25956
25957     // doc'ed in Field
25958     focus : function(){
25959         if(this.win && !this.sourceEditMode){
25960             this.win.focus();
25961         }else{
25962             this.el.focus();
25963         }
25964     },
25965     
25966     assignDocWin: function()
25967     {
25968         var iframe = this.iframe;
25969         
25970          if(Roo.isIE){
25971             this.doc = iframe.contentWindow.document;
25972             this.win = iframe.contentWindow;
25973         } else {
25974 //            if (!Roo.get(this.frameId)) {
25975 //                return;
25976 //            }
25977 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25978 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25979             
25980             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25981                 return;
25982             }
25983             
25984             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25985             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25986         }
25987     },
25988     
25989     // private
25990     initEditor : function(){
25991         //console.log("INIT EDITOR");
25992         this.assignDocWin();
25993         
25994         
25995         
25996         this.doc.designMode="on";
25997         this.doc.open();
25998         this.doc.write(this.getDocMarkup());
25999         this.doc.close();
26000         
26001         var dbody = (this.doc.body || this.doc.documentElement);
26002         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26003         // this copies styles from the containing element into thsi one..
26004         // not sure why we need all of this..
26005         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26006         
26007         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26008         //ss['background-attachment'] = 'fixed'; // w3c
26009         dbody.bgProperties = 'fixed'; // ie
26010         dbody.setAttribute("translate", "no");
26011         
26012         //Roo.DomHelper.applyStyles(dbody, ss);
26013         Roo.EventManager.on(this.doc, {
26014              
26015             'mouseup': this.onEditorEvent,
26016             'dblclick': this.onEditorEvent,
26017             'click': this.onEditorEvent,
26018             'keyup': this.onEditorEvent,
26019             
26020             buffer:100,
26021             scope: this
26022         });
26023         Roo.EventManager.on(this.doc, {
26024             'paste': this.onPasteEvent,
26025             scope : this
26026         });
26027         if(Roo.isGecko){
26028             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26029         }
26030         //??? needed???
26031         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26032             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26033         }
26034         this.initialized = true;
26035
26036         
26037         // initialize special key events - enter
26038         new Roo.htmleditor.KeyEnter({core : this});
26039         
26040          
26041         
26042         this.owner.fireEvent('initialize', this);
26043         this.pushValue();
26044     },
26045     // this is to prevent a href clicks resulting in a redirect?
26046    
26047     onPasteEvent : function(e,v)
26048     {
26049         // I think we better assume paste is going to be a dirty load of rubish from word..
26050         
26051         // even pasting into a 'email version' of this widget will have to clean up that mess.
26052         var cd = (e.browserEvent.clipboardData || window.clipboardData);
26053         
26054         // check what type of paste - if it's an image, then handle it differently.
26055         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
26056             // pasting images? 
26057             var urlAPI = (window.createObjectURL && window) || 
26058                 (window.URL && URL.revokeObjectURL && URL) || 
26059                 (window.webkitURL && webkitURL);
26060             
26061             var r = new FileReader();
26062             var t = this;
26063             r.addEventListener('load',function()
26064             {
26065                 
26066                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
26067                 // is insert asycn?
26068                 if (t.enableBlocks) {
26069                     
26070                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
26071                         if (img.closest('figure')) { // assume!! that it's aready
26072                             return;
26073                         }
26074                         var fig  = new Roo.htmleditor.BlockFigure({
26075                             image_src  : img.src
26076                         });
26077                         fig.updateElement(img); // replace it..
26078                         
26079                     });
26080                 }
26081                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
26082                 t.owner.fireEvent('paste', this);
26083             });
26084             r.readAsDataURL(cd.files[0]);
26085             
26086             e.preventDefault();
26087             
26088             return false;
26089         }
26090         if (cd.types.indexOf('text/html') < 0 ) {
26091             return false;
26092         }
26093         var images = [];
26094         var html = cd.getData('text/html'); // clipboard event
26095         if (cd.types.indexOf('text/rtf') > -1) {
26096             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
26097             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
26098         }
26099         // Roo.log(images);
26100         // Roo.log(imgs);
26101         // fixme..
26102         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
26103                        .map(function(g) { return g.toDataURL(); })
26104                        .filter(function(g) { return g != 'about:blank'; });
26105         
26106         //Roo.log(html);
26107         html = this.cleanWordChars(html);
26108         
26109         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
26110         
26111         
26112         var sn = this.getParentElement();
26113         // check if d contains a table, and prevent nesting??
26114         //Roo.log(d.getElementsByTagName('table'));
26115         //Roo.log(sn);
26116         //Roo.log(sn.closest('table'));
26117         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
26118             e.preventDefault();
26119             this.insertAtCursor("You can not nest tables");
26120             //Roo.log("prevent?"); // fixme - 
26121             return false;
26122         }
26123         
26124         
26125         
26126         if (images.length > 0) {
26127             // replace all v:imagedata - with img.
26128             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
26129             Roo.each(ar, function(node) {
26130                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
26131                 node.parentNode.removeChild(node);
26132             });
26133             
26134             
26135             Roo.each(d.getElementsByTagName('img'), function(img, i) {
26136                 img.setAttribute('src', images[i]);
26137             });
26138         }
26139         if (this.autoClean) {
26140             new Roo.htmleditor.FilterWord({ node : d });
26141             
26142             new Roo.htmleditor.FilterStyleToTag({ node : d });
26143             new Roo.htmleditor.FilterAttributes({
26144                 node : d,
26145                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
26146                 attrib_clean : ['href', 'src' ] 
26147             });
26148             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
26149             // should be fonts..
26150             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
26151             new Roo.htmleditor.FilterParagraph({ node : d });
26152             new Roo.htmleditor.FilterSpan({ node : d });
26153             new Roo.htmleditor.FilterLongBr({ node : d });
26154             new Roo.htmleditor.FilterComment({ node : d });
26155             
26156             
26157         }
26158         if (this.enableBlocks) {
26159                 
26160             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
26161                 if (img.closest('figure')) { // assume!! that it's aready
26162                     return;
26163                 }
26164                 var fig  = new Roo.htmleditor.BlockFigure({
26165                     image_src  : img.src
26166                 });
26167                 fig.updateElement(img); // replace it..
26168                 
26169             });
26170         }
26171         
26172         
26173         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
26174         if (this.enableBlocks) {
26175             Roo.htmleditor.Block.initAll(this.doc.body);
26176         }
26177          
26178         
26179         e.preventDefault();
26180         this.owner.fireEvent('paste', this);
26181         return false;
26182         // default behaveiour should be our local cleanup paste? (optional?)
26183         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
26184         //this.owner.fireEvent('paste', e, v);
26185     },
26186     // private
26187     onDestroy : function(){
26188         
26189         
26190         
26191         if(this.rendered){
26192             
26193             //for (var i =0; i < this.toolbars.length;i++) {
26194             //    // fixme - ask toolbars for heights?
26195             //    this.toolbars[i].onDestroy();
26196            // }
26197             
26198             //this.wrap.dom.innerHTML = '';
26199             //this.wrap.remove();
26200         }
26201     },
26202
26203     // private
26204     onFirstFocus : function(){
26205         
26206         this.assignDocWin();
26207         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
26208         
26209         this.activated = true;
26210          
26211     
26212         if(Roo.isGecko){ // prevent silly gecko errors
26213             this.win.focus();
26214             var s = this.win.getSelection();
26215             if(!s.focusNode || s.focusNode.nodeType != 3){
26216                 var r = s.getRangeAt(0);
26217                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26218                 r.collapse(true);
26219                 this.deferFocus();
26220             }
26221             try{
26222                 this.execCmd('useCSS', true);
26223                 this.execCmd('styleWithCSS', false);
26224             }catch(e){}
26225         }
26226         this.owner.fireEvent('activate', this);
26227     },
26228
26229     // private
26230     adjustFont: function(btn){
26231         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26232         //if(Roo.isSafari){ // safari
26233         //    adjust *= 2;
26234        // }
26235         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26236         if(Roo.isSafari){ // safari
26237             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26238             v =  (v < 10) ? 10 : v;
26239             v =  (v > 48) ? 48 : v;
26240             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26241             
26242         }
26243         
26244         
26245         v = Math.max(1, v+adjust);
26246         
26247         this.execCmd('FontSize', v  );
26248     },
26249
26250     onEditorEvent : function(e)
26251     {
26252          
26253         
26254         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
26255             return; // we do not handle this.. (undo manager does..)
26256         }
26257         // clicking a 'block'?
26258         
26259         // in theory this detects if the last element is not a br, then we try and do that.
26260         // its so clicking in space at bottom triggers adding a br and moving the cursor.
26261         if (e &&
26262             e.target.nodeName == 'BODY' &&
26263             e.type == "mouseup" &&
26264             this.doc.body.lastChild
26265            ) {
26266             var lc = this.doc.body.lastChild;
26267             // gtx-trans is google translate plugin adding crap.
26268             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
26269                 lc = lc.previousSibling;
26270             }
26271             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
26272             // if last element is <BR> - then dont do anything.
26273             
26274                 var ns = this.doc.createElement('br');
26275                 this.doc.body.appendChild(ns);
26276                 range = this.doc.createRange();
26277                 range.setStartAfter(ns);
26278                 range.collapse(true);
26279                 var sel = this.win.getSelection();
26280                 sel.removeAllRanges();
26281                 sel.addRange(range);
26282             }
26283         }
26284         
26285         
26286         
26287         this.fireEditorEvent(e);
26288       //  this.updateToolbar();
26289         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26290     },
26291     
26292     fireEditorEvent: function(e)
26293     {
26294         this.owner.fireEvent('editorevent', this, e);
26295     },
26296
26297     insertTag : function(tg)
26298     {
26299         // could be a bit smarter... -> wrap the current selected tRoo..
26300         if (tg.toLowerCase() == 'span' ||
26301             tg.toLowerCase() == 'code' ||
26302             tg.toLowerCase() == 'sup' ||
26303             tg.toLowerCase() == 'sub' 
26304             ) {
26305             
26306             range = this.createRange(this.getSelection());
26307             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26308             wrappingNode.appendChild(range.extractContents());
26309             range.insertNode(wrappingNode);
26310
26311             return;
26312             
26313             
26314             
26315         }
26316         this.execCmd("formatblock",   tg);
26317         this.undoManager.addEvent(); 
26318     },
26319     
26320     insertText : function(txt)
26321     {
26322         
26323         
26324         var range = this.createRange();
26325         range.deleteContents();
26326                //alert(Sender.getAttribute('label'));
26327                
26328         range.insertNode(this.doc.createTextNode(txt));
26329         this.undoManager.addEvent();
26330     } ,
26331     
26332      
26333
26334     /**
26335      * Executes a Midas editor command on the editor document and performs necessary focus and
26336      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26337      * @param {String} cmd The Midas command
26338      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26339      */
26340     relayCmd : function(cmd, value)
26341     {
26342         
26343         switch (cmd) {
26344             case 'justifyleft':
26345             case 'justifyright':
26346             case 'justifycenter':
26347                 // if we are in a cell, then we will adjust the
26348                 var n = this.getParentElement();
26349                 var td = n.closest('td');
26350                 if (td) {
26351                     var bl = Roo.htmleditor.Block.factory(td);
26352                     bl.textAlign = cmd.replace('justify','');
26353                     bl.updateElement();
26354                     this.owner.fireEvent('editorevent', this);
26355                     return;
26356                 }
26357                 this.execCmd('styleWithCSS', true); // 
26358                 break;
26359             case 'bold':
26360             case 'italic':
26361             case 'underline':                
26362                 // if there is no selection, then we insert, and set the curson inside it..
26363                 this.execCmd('styleWithCSS', false); 
26364                 break;
26365                 
26366         
26367             default:
26368                 break;
26369         }
26370         
26371         
26372         this.win.focus();
26373         this.execCmd(cmd, value);
26374         this.owner.fireEvent('editorevent', this);
26375         //this.updateToolbar();
26376         this.owner.deferFocus();
26377     },
26378
26379     /**
26380      * Executes a Midas editor command directly on the editor document.
26381      * For visual commands, you should use {@link #relayCmd} instead.
26382      * <b>This should only be called after the editor is initialized.</b>
26383      * @param {String} cmd The Midas command
26384      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26385      */
26386     execCmd : function(cmd, value){
26387         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26388         this.syncValue();
26389     },
26390  
26391  
26392    
26393     /**
26394      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26395      * to insert tRoo.
26396      * @param {String} text | dom node.. 
26397      */
26398     insertAtCursor : function(text)
26399     {
26400         
26401         if(!this.activated){
26402             return;
26403         }
26404          
26405         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26406             this.win.focus();
26407             
26408             
26409             // from jquery ui (MIT licenced)
26410             var range, node;
26411             var win = this.win;
26412             
26413             if (win.getSelection && win.getSelection().getRangeAt) {
26414                 
26415                 // delete the existing?
26416                 
26417                 this.createRange(this.getSelection()).deleteContents();
26418                 range = win.getSelection().getRangeAt(0);
26419                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26420                 range.insertNode(node);
26421                 range = range.cloneRange();
26422                 range.collapse(false);
26423                  
26424                 win.getSelection().removeAllRanges();
26425                 win.getSelection().addRange(range);
26426                 
26427                 
26428                 
26429             } else if (win.document.selection && win.document.selection.createRange) {
26430                 // no firefox support
26431                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26432                 win.document.selection.createRange().pasteHTML(txt);
26433             
26434             } else {
26435                 // no firefox support
26436                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26437                 this.execCmd('InsertHTML', txt);
26438             } 
26439             this.syncValue();
26440             
26441             this.deferFocus();
26442         }
26443     },
26444  // private
26445     mozKeyPress : function(e){
26446         if(e.ctrlKey){
26447             var c = e.getCharCode(), cmd;
26448           
26449             if(c > 0){
26450                 c = String.fromCharCode(c).toLowerCase();
26451                 switch(c){
26452                     case 'b':
26453                         cmd = 'bold';
26454                         break;
26455                     case 'i':
26456                         cmd = 'italic';
26457                         break;
26458                     
26459                     case 'u':
26460                         cmd = 'underline';
26461                         break;
26462                     
26463                     //case 'v':
26464                       //  this.cleanUpPaste.defer(100, this);
26465                       //  return;
26466                         
26467                 }
26468                 if(cmd){
26469                     
26470                     this.relayCmd(cmd);
26471                     //this.win.focus();
26472                     //this.execCmd(cmd);
26473                     //this.deferFocus();
26474                     e.preventDefault();
26475                 }
26476                 
26477             }
26478         }
26479     },
26480
26481     // private
26482     fixKeys : function(){ // load time branching for fastest keydown performance
26483         
26484         
26485         if(Roo.isIE){
26486             return function(e){
26487                 var k = e.getKey(), r;
26488                 if(k == e.TAB){
26489                     e.stopEvent();
26490                     r = this.doc.selection.createRange();
26491                     if(r){
26492                         r.collapse(true);
26493                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26494                         this.deferFocus();
26495                     }
26496                     return;
26497                 }
26498                 /// this is handled by Roo.htmleditor.KeyEnter
26499                  /*
26500                 if(k == e.ENTER){
26501                     r = this.doc.selection.createRange();
26502                     if(r){
26503                         var target = r.parentElement();
26504                         if(!target || target.tagName.toLowerCase() != 'li'){
26505                             e.stopEvent();
26506                             r.pasteHTML('<br/>');
26507                             r.collapse(false);
26508                             r.select();
26509                         }
26510                     }
26511                 }
26512                 */
26513                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26514                 //    this.cleanUpPaste.defer(100, this);
26515                 //    return;
26516                 //}
26517                 
26518                 
26519             };
26520         }else if(Roo.isOpera){
26521             return function(e){
26522                 var k = e.getKey();
26523                 if(k == e.TAB){
26524                     e.stopEvent();
26525                     this.win.focus();
26526                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26527                     this.deferFocus();
26528                 }
26529                
26530                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26531                 //    this.cleanUpPaste.defer(100, this);
26532                  //   return;
26533                 //}
26534                 
26535             };
26536         }else if(Roo.isSafari){
26537             return function(e){
26538                 var k = e.getKey();
26539                 
26540                 if(k == e.TAB){
26541                     e.stopEvent();
26542                     this.execCmd('InsertText','\t');
26543                     this.deferFocus();
26544                     return;
26545                 }
26546                  this.mozKeyPress(e);
26547                 
26548                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26549                  //   this.cleanUpPaste.defer(100, this);
26550                  //   return;
26551                // }
26552                 
26553              };
26554         }
26555     }(),
26556     
26557     getAllAncestors: function()
26558     {
26559         var p = this.getSelectedNode();
26560         var a = [];
26561         if (!p) {
26562             a.push(p); // push blank onto stack..
26563             p = this.getParentElement();
26564         }
26565         
26566         
26567         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26568             a.push(p);
26569             p = p.parentNode;
26570         }
26571         a.push(this.doc.body);
26572         return a;
26573     },
26574     lastSel : false,
26575     lastSelNode : false,
26576     
26577     
26578     getSelection : function() 
26579     {
26580         this.assignDocWin();
26581         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26582     },
26583     /**
26584      * Select a dom node
26585      * @param {DomElement} node the node to select
26586      */
26587     selectNode : function(node, collapse)
26588     {
26589         var nodeRange = node.ownerDocument.createRange();
26590         try {
26591             nodeRange.selectNode(node);
26592         } catch (e) {
26593             nodeRange.selectNodeContents(node);
26594         }
26595         if (collapse === true) {
26596             nodeRange.collapse(true);
26597         }
26598         //
26599         var s = this.win.getSelection();
26600         s.removeAllRanges();
26601         s.addRange(nodeRange);
26602     },
26603     
26604     getSelectedNode: function() 
26605     {
26606         // this may only work on Gecko!!!
26607         
26608         // should we cache this!!!!
26609         
26610          
26611          
26612         var range = this.createRange(this.getSelection()).cloneRange();
26613         
26614         if (Roo.isIE) {
26615             var parent = range.parentElement();
26616             while (true) {
26617                 var testRange = range.duplicate();
26618                 testRange.moveToElementText(parent);
26619                 if (testRange.inRange(range)) {
26620                     break;
26621                 }
26622                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26623                     break;
26624                 }
26625                 parent = parent.parentElement;
26626             }
26627             return parent;
26628         }
26629         
26630         // is ancestor a text element.
26631         var ac =  range.commonAncestorContainer;
26632         if (ac.nodeType == 3) {
26633             ac = ac.parentNode;
26634         }
26635         
26636         var ar = ac.childNodes;
26637          
26638         var nodes = [];
26639         var other_nodes = [];
26640         var has_other_nodes = false;
26641         for (var i=0;i<ar.length;i++) {
26642             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26643                 continue;
26644             }
26645             // fullly contained node.
26646             
26647             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26648                 nodes.push(ar[i]);
26649                 continue;
26650             }
26651             
26652             // probably selected..
26653             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26654                 other_nodes.push(ar[i]);
26655                 continue;
26656             }
26657             // outer..
26658             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26659                 continue;
26660             }
26661             
26662             
26663             has_other_nodes = true;
26664         }
26665         if (!nodes.length && other_nodes.length) {
26666             nodes= other_nodes;
26667         }
26668         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26669             return false;
26670         }
26671         
26672         return nodes[0];
26673     },
26674     
26675     
26676     createRange: function(sel)
26677     {
26678         // this has strange effects when using with 
26679         // top toolbar - not sure if it's a great idea.
26680         //this.editor.contentWindow.focus();
26681         if (typeof sel != "undefined") {
26682             try {
26683                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26684             } catch(e) {
26685                 return this.doc.createRange();
26686             }
26687         } else {
26688             return this.doc.createRange();
26689         }
26690     },
26691     getParentElement: function()
26692     {
26693         
26694         this.assignDocWin();
26695         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26696         
26697         var range = this.createRange(sel);
26698          
26699         try {
26700             var p = range.commonAncestorContainer;
26701             while (p.nodeType == 3) { // text node
26702                 p = p.parentNode;
26703             }
26704             return p;
26705         } catch (e) {
26706             return null;
26707         }
26708     
26709     },
26710     /***
26711      *
26712      * Range intersection.. the hard stuff...
26713      *  '-1' = before
26714      *  '0' = hits..
26715      *  '1' = after.
26716      *         [ -- selected range --- ]
26717      *   [fail]                        [fail]
26718      *
26719      *    basically..
26720      *      if end is before start or  hits it. fail.
26721      *      if start is after end or hits it fail.
26722      *
26723      *   if either hits (but other is outside. - then it's not 
26724      *   
26725      *    
26726      **/
26727     
26728     
26729     // @see http://www.thismuchiknow.co.uk/?p=64.
26730     rangeIntersectsNode : function(range, node)
26731     {
26732         var nodeRange = node.ownerDocument.createRange();
26733         try {
26734             nodeRange.selectNode(node);
26735         } catch (e) {
26736             nodeRange.selectNodeContents(node);
26737         }
26738     
26739         var rangeStartRange = range.cloneRange();
26740         rangeStartRange.collapse(true);
26741     
26742         var rangeEndRange = range.cloneRange();
26743         rangeEndRange.collapse(false);
26744     
26745         var nodeStartRange = nodeRange.cloneRange();
26746         nodeStartRange.collapse(true);
26747     
26748         var nodeEndRange = nodeRange.cloneRange();
26749         nodeEndRange.collapse(false);
26750     
26751         return rangeStartRange.compareBoundaryPoints(
26752                  Range.START_TO_START, nodeEndRange) == -1 &&
26753                rangeEndRange.compareBoundaryPoints(
26754                  Range.START_TO_START, nodeStartRange) == 1;
26755         
26756          
26757     },
26758     rangeCompareNode : function(range, node)
26759     {
26760         var nodeRange = node.ownerDocument.createRange();
26761         try {
26762             nodeRange.selectNode(node);
26763         } catch (e) {
26764             nodeRange.selectNodeContents(node);
26765         }
26766         
26767         
26768         range.collapse(true);
26769     
26770         nodeRange.collapse(true);
26771      
26772         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26773         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26774          
26775         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26776         
26777         var nodeIsBefore   =  ss == 1;
26778         var nodeIsAfter    = ee == -1;
26779         
26780         if (nodeIsBefore && nodeIsAfter) {
26781             return 0; // outer
26782         }
26783         if (!nodeIsBefore && nodeIsAfter) {
26784             return 1; //right trailed.
26785         }
26786         
26787         if (nodeIsBefore && !nodeIsAfter) {
26788             return 2;  // left trailed.
26789         }
26790         // fully contined.
26791         return 3;
26792     },
26793  
26794     cleanWordChars : function(input) {// change the chars to hex code
26795         
26796        var swapCodes  = [ 
26797             [    8211, "&#8211;" ], 
26798             [    8212, "&#8212;" ], 
26799             [    8216,  "'" ],  
26800             [    8217, "'" ],  
26801             [    8220, '"' ],  
26802             [    8221, '"' ],  
26803             [    8226, "*" ],  
26804             [    8230, "..." ]
26805         ]; 
26806         var output = input;
26807         Roo.each(swapCodes, function(sw) { 
26808             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26809             
26810             output = output.replace(swapper, sw[1]);
26811         });
26812         
26813         return output;
26814     },
26815     
26816      
26817     
26818         
26819     
26820     cleanUpChild : function (node)
26821     {
26822         
26823         new Roo.htmleditor.FilterComment({node : node});
26824         new Roo.htmleditor.FilterAttributes({
26825                 node : node,
26826                 attrib_black : this.ablack,
26827                 attrib_clean : this.aclean,
26828                 style_white : this.cwhite,
26829                 style_black : this.cblack
26830         });
26831         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26832         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26833          
26834         
26835     },
26836     
26837     /**
26838      * Clean up MS wordisms...
26839      * @deprecated - use filter directly
26840      */
26841     cleanWord : function(node)
26842     {
26843         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26844         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
26845         
26846     },
26847    
26848     
26849     /**
26850
26851      * @deprecated - use filters
26852      */
26853     cleanTableWidths : function(node)
26854     {
26855         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26856         
26857  
26858     },
26859     
26860      
26861         
26862     applyBlacklists : function()
26863     {
26864         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26865         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26866         
26867         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
26868         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
26869         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
26870         
26871         this.white = [];
26872         this.black = [];
26873         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26874             if (b.indexOf(tag) > -1) {
26875                 return;
26876             }
26877             this.white.push(tag);
26878             
26879         }, this);
26880         
26881         Roo.each(w, function(tag) {
26882             if (b.indexOf(tag) > -1) {
26883                 return;
26884             }
26885             if (this.white.indexOf(tag) > -1) {
26886                 return;
26887             }
26888             this.white.push(tag);
26889             
26890         }, this);
26891         
26892         
26893         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26894             if (w.indexOf(tag) > -1) {
26895                 return;
26896             }
26897             this.black.push(tag);
26898             
26899         }, this);
26900         
26901         Roo.each(b, function(tag) {
26902             if (w.indexOf(tag) > -1) {
26903                 return;
26904             }
26905             if (this.black.indexOf(tag) > -1) {
26906                 return;
26907             }
26908             this.black.push(tag);
26909             
26910         }, this);
26911         
26912         
26913         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26914         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26915         
26916         this.cwhite = [];
26917         this.cblack = [];
26918         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26919             if (b.indexOf(tag) > -1) {
26920                 return;
26921             }
26922             this.cwhite.push(tag);
26923             
26924         }, this);
26925         
26926         Roo.each(w, function(tag) {
26927             if (b.indexOf(tag) > -1) {
26928                 return;
26929             }
26930             if (this.cwhite.indexOf(tag) > -1) {
26931                 return;
26932             }
26933             this.cwhite.push(tag);
26934             
26935         }, this);
26936         
26937         
26938         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26939             if (w.indexOf(tag) > -1) {
26940                 return;
26941             }
26942             this.cblack.push(tag);
26943             
26944         }, this);
26945         
26946         Roo.each(b, function(tag) {
26947             if (w.indexOf(tag) > -1) {
26948                 return;
26949             }
26950             if (this.cblack.indexOf(tag) > -1) {
26951                 return;
26952             }
26953             this.cblack.push(tag);
26954             
26955         }, this);
26956     },
26957     
26958     setStylesheets : function(stylesheets)
26959     {
26960         if(typeof(stylesheets) == 'string'){
26961             Roo.get(this.iframe.contentDocument.head).createChild({
26962                 tag : 'link',
26963                 rel : 'stylesheet',
26964                 type : 'text/css',
26965                 href : stylesheets
26966             });
26967             
26968             return;
26969         }
26970         var _this = this;
26971      
26972         Roo.each(stylesheets, function(s) {
26973             if(!s.length){
26974                 return;
26975             }
26976             
26977             Roo.get(_this.iframe.contentDocument.head).createChild({
26978                 tag : 'link',
26979                 rel : 'stylesheet',
26980                 type : 'text/css',
26981                 href : s
26982             });
26983         });
26984
26985         
26986     },
26987     
26988     
26989     updateLanguage : function()
26990     {
26991         if (!this.iframe || !this.iframe.contentDocument) {
26992             return;
26993         }
26994         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26995     },
26996     
26997     
26998     removeStylesheets : function()
26999     {
27000         var _this = this;
27001         
27002         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27003             s.remove();
27004         });
27005     },
27006     
27007     setStyle : function(style)
27008     {
27009         Roo.get(this.iframe.contentDocument.head).createChild({
27010             tag : 'style',
27011             type : 'text/css',
27012             html : style
27013         });
27014
27015         return;
27016     }
27017     
27018     // hide stuff that is not compatible
27019     /**
27020      * @event blur
27021      * @hide
27022      */
27023     /**
27024      * @event change
27025      * @hide
27026      */
27027     /**
27028      * @event focus
27029      * @hide
27030      */
27031     /**
27032      * @event specialkey
27033      * @hide
27034      */
27035     /**
27036      * @cfg {String} fieldClass @hide
27037      */
27038     /**
27039      * @cfg {String} focusClass @hide
27040      */
27041     /**
27042      * @cfg {String} autoCreate @hide
27043      */
27044     /**
27045      * @cfg {String} inputType @hide
27046      */
27047     /**
27048      * @cfg {String} invalidClass @hide
27049      */
27050     /**
27051      * @cfg {String} invalidText @hide
27052      */
27053     /**
27054      * @cfg {String} msgFx @hide
27055      */
27056     /**
27057      * @cfg {String} validateOnBlur @hide
27058      */
27059 });
27060
27061 Roo.HtmlEditorCore.white = [
27062         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
27063         
27064        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
27065        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
27066        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
27067        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
27068        'TABLE',   'UL',         'XMP', 
27069        
27070        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
27071       'THEAD',   'TR', 
27072      
27073       'DIR', 'MENU', 'OL', 'UL', 'DL',
27074        
27075       'EMBED',  'OBJECT'
27076 ];
27077
27078
27079 Roo.HtmlEditorCore.black = [
27080     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27081         'APPLET', // 
27082         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
27083         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
27084         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
27085         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
27086         //'FONT' // CLEAN LATER..
27087         'COLGROUP', 'COL'   // messy tables.
27088         
27089         
27090 ];
27091 Roo.HtmlEditorCore.clean = [ // ?? needed???
27092      'SCRIPT', 'STYLE', 'TITLE', 'XML'
27093 ];
27094 Roo.HtmlEditorCore.tag_remove = [
27095     'FONT', 'TBODY'  
27096 ];
27097 // attributes..
27098
27099 Roo.HtmlEditorCore.ablack = [
27100     'on'
27101 ];
27102     
27103 Roo.HtmlEditorCore.aclean = [ 
27104     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27105 ];
27106
27107 // protocols..
27108 Roo.HtmlEditorCore.pwhite= [
27109         'http',  'https',  'mailto'
27110 ];
27111
27112 // white listed style attributes.
27113 Roo.HtmlEditorCore.cwhite= [
27114       //  'text-align', /// default is to allow most things..
27115       
27116          
27117 //        'font-size'//??
27118 ];
27119
27120 // black listed style attributes.
27121 Roo.HtmlEditorCore.cblack= [
27122       //  'font-size' -- this can be set by the project 
27123 ];
27124
27125
27126
27127
27128     //<script type="text/javascript">
27129
27130 /*
27131  * Ext JS Library 1.1.1
27132  * Copyright(c) 2006-2007, Ext JS, LLC.
27133  * Licence LGPL
27134  * 
27135  */
27136  
27137  
27138 Roo.form.HtmlEditor = function(config){
27139     
27140     
27141     
27142     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
27143     
27144     if (!this.toolbars) {
27145         this.toolbars = [];
27146     }
27147     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27148     
27149     
27150 };
27151
27152 /**
27153  * @class Roo.form.HtmlEditor
27154  * @extends Roo.form.Field
27155  * Provides a lightweight HTML Editor component.
27156  *
27157  * This has been tested on Fireforx / Chrome.. IE may not be so great..
27158  * 
27159  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
27160  * supported by this editor.</b><br/><br/>
27161  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
27162  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
27163  */
27164 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
27165     /**
27166      * @cfg {Boolean} clearUp
27167      */
27168     clearUp : true,
27169       /**
27170      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27171      */
27172     toolbars : false,
27173    
27174      /**
27175      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27176      *                        Roo.resizable.
27177      */
27178     resizable : false,
27179      /**
27180      * @cfg {Number} height (in pixels)
27181      */   
27182     height: 300,
27183    /**
27184      * @cfg {Number} width (in pixels)
27185      */   
27186     width: 500,
27187     
27188     /**
27189      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
27190      * 
27191      */
27192     stylesheets: false,
27193     
27194     
27195      /**
27196      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
27197      * 
27198      */
27199     cblack: false,
27200     /**
27201      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
27202      * 
27203      */
27204     cwhite: false,
27205     
27206      /**
27207      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
27208      * 
27209      */
27210     black: false,
27211     /**
27212      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
27213      * 
27214      */
27215     white: false,
27216     /**
27217      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
27218      */
27219     allowComments: false,
27220     /**
27221      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
27222      */
27223     enableBlocks : true,
27224     
27225     /**
27226      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
27227      *         if you are doing an email editor, this probably needs disabling, it's designed
27228      */
27229     autoClean: true,
27230     /**
27231      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
27232      */
27233     bodyCls : '',
27234     /**
27235      * @cfg {String} language default en - language of text (usefull for rtl languages)
27236      * 
27237      */
27238     language: 'en',
27239     
27240      
27241     // id of frame..
27242     frameId: false,
27243     
27244     // private properties
27245     validationEvent : false,
27246     deferHeight: true,
27247     initialized : false,
27248     activated : false,
27249     
27250     onFocus : Roo.emptyFn,
27251     iframePad:3,
27252     hideMode:'offsets',
27253     
27254     actionMode : 'container', // defaults to hiding it...
27255     
27256     defaultAutoCreate : { // modified by initCompnoent..
27257         tag: "textarea",
27258         style:"width:500px;height:300px;",
27259         autocomplete: "new-password"
27260     },
27261
27262     // private
27263     initComponent : function(){
27264         this.addEvents({
27265             /**
27266              * @event initialize
27267              * Fires when the editor is fully initialized (including the iframe)
27268              * @param {HtmlEditor} this
27269              */
27270             initialize: true,
27271             /**
27272              * @event activate
27273              * Fires when the editor is first receives the focus. Any insertion must wait
27274              * until after this event.
27275              * @param {HtmlEditor} this
27276              */
27277             activate: true,
27278              /**
27279              * @event beforesync
27280              * Fires before the textarea is updated with content from the editor iframe. Return false
27281              * to cancel the sync.
27282              * @param {HtmlEditor} this
27283              * @param {String} html
27284              */
27285             beforesync: true,
27286              /**
27287              * @event beforepush
27288              * Fires before the iframe editor is updated with content from the textarea. Return false
27289              * to cancel the push.
27290              * @param {HtmlEditor} this
27291              * @param {String} html
27292              */
27293             beforepush: true,
27294              /**
27295              * @event sync
27296              * Fires when the textarea is updated with content from the editor iframe.
27297              * @param {HtmlEditor} this
27298              * @param {String} html
27299              */
27300             sync: true,
27301              /**
27302              * @event push
27303              * Fires when the iframe editor is updated with content from the textarea.
27304              * @param {HtmlEditor} this
27305              * @param {String} html
27306              */
27307             push: true,
27308              /**
27309              * @event editmodechange
27310              * Fires when the editor switches edit modes
27311              * @param {HtmlEditor} this
27312              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27313              */
27314             editmodechange: true,
27315             /**
27316              * @event editorevent
27317              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27318              * @param {HtmlEditor} this
27319              */
27320             editorevent: true,
27321             /**
27322              * @event firstfocus
27323              * Fires when on first focus - needed by toolbars..
27324              * @param {HtmlEditor} this
27325              */
27326             firstfocus: true,
27327             /**
27328              * @event autosave
27329              * Auto save the htmlEditor value as a file into Events
27330              * @param {HtmlEditor} this
27331              */
27332             autosave: true,
27333             /**
27334              * @event savedpreview
27335              * preview the saved version of htmlEditor
27336              * @param {HtmlEditor} this
27337              */
27338             savedpreview: true,
27339             
27340             /**
27341             * @event stylesheetsclick
27342             * Fires when press the Sytlesheets button
27343             * @param {Roo.HtmlEditorCore} this
27344             */
27345             stylesheetsclick: true,
27346             /**
27347             * @event paste
27348             * Fires when press user pastes into the editor
27349             * @param {Roo.HtmlEditorCore} this
27350             */
27351             paste: true 
27352             
27353         });
27354         this.defaultAutoCreate =  {
27355             tag: "textarea",
27356             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
27357             autocomplete: "new-password"
27358         };
27359     },
27360
27361     /**
27362      * Protected method that will not generally be called directly. It
27363      * is called when the editor creates its toolbar. Override this method if you need to
27364      * add custom toolbar buttons.
27365      * @param {HtmlEditor} editor
27366      */
27367     createToolbar : function(editor){
27368         Roo.log("create toolbars");
27369         if (!editor.toolbars || !editor.toolbars.length) {
27370             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
27371         }
27372         
27373         for (var i =0 ; i < editor.toolbars.length;i++) {
27374             editor.toolbars[i] = Roo.factory(
27375                     typeof(editor.toolbars[i]) == 'string' ?
27376                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
27377                 Roo.form.HtmlEditor);
27378             editor.toolbars[i].init(editor);
27379         }
27380          
27381         
27382     },
27383     /**
27384      * get the Context selected node
27385      * @returns {DomElement|boolean} selected node if active or false if none
27386      * 
27387      */
27388     getSelectedNode : function()
27389     {
27390         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
27391             return false;
27392         }
27393         return this.toolbars[1].tb.selectedNode;
27394     
27395     },
27396     // private
27397     onRender : function(ct, position)
27398     {
27399         var _t = this;
27400         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27401         
27402         this.wrap = this.el.wrap({
27403             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27404         });
27405         
27406         this.editorcore.onRender(ct, position);
27407          
27408         if (this.resizable) {
27409             this.resizeEl = new Roo.Resizable(this.wrap, {
27410                 pinned : true,
27411                 wrap: true,
27412                 dynamic : true,
27413                 minHeight : this.height,
27414                 height: this.height,
27415                 handles : this.resizable,
27416                 width: this.width,
27417                 listeners : {
27418                     resize : function(r, w, h) {
27419                         _t.onResize(w,h); // -something
27420                     }
27421                 }
27422             });
27423             
27424         }
27425         this.createToolbar(this);
27426        
27427         
27428         if(!this.width){
27429             this.setSize(this.wrap.getSize());
27430         }
27431         if (this.resizeEl) {
27432             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27433             // should trigger onReize..
27434         }
27435         
27436         this.keyNav = new Roo.KeyNav(this.el, {
27437             
27438             "tab" : function(e){
27439                 e.preventDefault();
27440                 
27441                 var value = this.getValue();
27442                 
27443                 var start = this.el.dom.selectionStart;
27444                 var end = this.el.dom.selectionEnd;
27445                 
27446                 if(!e.shiftKey){
27447                     
27448                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
27449                     this.el.dom.setSelectionRange(end + 1, end + 1);
27450                     return;
27451                 }
27452                 
27453                 var f = value.substring(0, start).split("\t");
27454                 
27455                 if(f.pop().length != 0){
27456                     return;
27457                 }
27458                 
27459                 this.setValue(f.join("\t") + value.substring(end));
27460                 this.el.dom.setSelectionRange(start - 1, start - 1);
27461                 
27462             },
27463             
27464             "home" : function(e){
27465                 e.preventDefault();
27466                 
27467                 var curr = this.el.dom.selectionStart;
27468                 var lines = this.getValue().split("\n");
27469                 
27470                 if(!lines.length){
27471                     return;
27472                 }
27473                 
27474                 if(e.ctrlKey){
27475                     this.el.dom.setSelectionRange(0, 0);
27476                     return;
27477                 }
27478                 
27479                 var pos = 0;
27480                 
27481                 for (var i = 0; i < lines.length;i++) {
27482                     pos += lines[i].length;
27483                     
27484                     if(i != 0){
27485                         pos += 1;
27486                     }
27487                     
27488                     if(pos < curr){
27489                         continue;
27490                     }
27491                     
27492                     pos -= lines[i].length;
27493                     
27494                     break;
27495                 }
27496                 
27497                 if(!e.shiftKey){
27498                     this.el.dom.setSelectionRange(pos, pos);
27499                     return;
27500                 }
27501                 
27502                 this.el.dom.selectionStart = pos;
27503                 this.el.dom.selectionEnd = curr;
27504             },
27505             
27506             "end" : function(e){
27507                 e.preventDefault();
27508                 
27509                 var curr = this.el.dom.selectionStart;
27510                 var lines = this.getValue().split("\n");
27511                 
27512                 if(!lines.length){
27513                     return;
27514                 }
27515                 
27516                 if(e.ctrlKey){
27517                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
27518                     return;
27519                 }
27520                 
27521                 var pos = 0;
27522                 
27523                 for (var i = 0; i < lines.length;i++) {
27524                     
27525                     pos += lines[i].length;
27526                     
27527                     if(i != 0){
27528                         pos += 1;
27529                     }
27530                     
27531                     if(pos < curr){
27532                         continue;
27533                     }
27534                     
27535                     break;
27536                 }
27537                 
27538                 if(!e.shiftKey){
27539                     this.el.dom.setSelectionRange(pos, pos);
27540                     return;
27541                 }
27542                 
27543                 this.el.dom.selectionStart = curr;
27544                 this.el.dom.selectionEnd = pos;
27545             },
27546
27547             scope : this,
27548
27549             doRelay : function(foo, bar, hname){
27550                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
27551             },
27552
27553             forceKeyDown: true
27554         });
27555         
27556 //        if(this.autosave && this.w){
27557 //            this.autoSaveFn = setInterval(this.autosave, 1000);
27558 //        }
27559     },
27560
27561     // private
27562     onResize : function(w, h)
27563     {
27564         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27565         var ew = false;
27566         var eh = false;
27567         
27568         if(this.el ){
27569             if(typeof w == 'number'){
27570                 var aw = w - this.wrap.getFrameWidth('lr');
27571                 this.el.setWidth(this.adjustWidth('textarea', aw));
27572                 ew = aw;
27573             }
27574             if(typeof h == 'number'){
27575                 var tbh = 0;
27576                 for (var i =0; i < this.toolbars.length;i++) {
27577                     // fixme - ask toolbars for heights?
27578                     tbh += this.toolbars[i].tb.el.getHeight();
27579                     if (this.toolbars[i].footer) {
27580                         tbh += this.toolbars[i].footer.el.getHeight();
27581                     }
27582                 }
27583                 
27584                 
27585                 
27586                 
27587                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27588                 ah -= 5; // knock a few pixes off for look..
27589 //                Roo.log(ah);
27590                 this.el.setHeight(this.adjustWidth('textarea', ah));
27591                 var eh = ah;
27592             }
27593         }
27594         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27595         this.editorcore.onResize(ew,eh);
27596         
27597     },
27598
27599     /**
27600      * Toggles the editor between standard and source edit mode.
27601      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27602      */
27603     toggleSourceEdit : function(sourceEditMode)
27604     {
27605         this.editorcore.toggleSourceEdit(sourceEditMode);
27606         
27607         if(this.editorcore.sourceEditMode){
27608             Roo.log('editor - showing textarea');
27609             
27610 //            Roo.log('in');
27611 //            Roo.log(this.syncValue());
27612             this.editorcore.syncValue();
27613             this.el.removeClass('x-hidden');
27614             this.el.dom.removeAttribute('tabIndex');
27615             this.el.focus();
27616             this.el.dom.scrollTop = 0;
27617             
27618             
27619             for (var i = 0; i < this.toolbars.length; i++) {
27620                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27621                     this.toolbars[i].tb.hide();
27622                     this.toolbars[i].footer.hide();
27623                 }
27624             }
27625             
27626         }else{
27627             Roo.log('editor - hiding textarea');
27628 //            Roo.log('out')
27629 //            Roo.log(this.pushValue()); 
27630             this.editorcore.pushValue();
27631             
27632             this.el.addClass('x-hidden');
27633             this.el.dom.setAttribute('tabIndex', -1);
27634             
27635             for (var i = 0; i < this.toolbars.length; i++) {
27636                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27637                     this.toolbars[i].tb.show();
27638                     this.toolbars[i].footer.show();
27639                 }
27640             }
27641             
27642             //this.deferFocus();
27643         }
27644         
27645         this.setSize(this.wrap.getSize());
27646         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27647         
27648         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27649     },
27650  
27651     // private (for BoxComponent)
27652     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27653
27654     // private (for BoxComponent)
27655     getResizeEl : function(){
27656         return this.wrap;
27657     },
27658
27659     // private (for BoxComponent)
27660     getPositionEl : function(){
27661         return this.wrap;
27662     },
27663
27664     // private
27665     initEvents : function(){
27666         this.originalValue = this.getValue();
27667     },
27668
27669     /**
27670      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27671      * @method
27672      */
27673     markInvalid : Roo.emptyFn,
27674     /**
27675      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27676      * @method
27677      */
27678     clearInvalid : Roo.emptyFn,
27679
27680     setValue : function(v){
27681         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27682         this.editorcore.pushValue();
27683     },
27684
27685     /**
27686      * update the language in the body - really done by core
27687      * @param {String} language - eg. en / ar / zh-CN etc..
27688      */
27689     updateLanguage : function(lang)
27690     {
27691         this.language = lang;
27692         this.editorcore.language = lang;
27693         this.editorcore.updateLanguage();
27694      
27695     },
27696     // private
27697     deferFocus : function(){
27698         this.focus.defer(10, this);
27699     },
27700
27701     // doc'ed in Field
27702     focus : function(){
27703         this.editorcore.focus();
27704         
27705     },
27706       
27707
27708     // private
27709     onDestroy : function(){
27710         
27711         
27712         
27713         if(this.rendered){
27714             
27715             for (var i =0; i < this.toolbars.length;i++) {
27716                 // fixme - ask toolbars for heights?
27717                 this.toolbars[i].onDestroy();
27718             }
27719             
27720             this.wrap.dom.innerHTML = '';
27721             this.wrap.remove();
27722         }
27723     },
27724
27725     // private
27726     onFirstFocus : function(){
27727         //Roo.log("onFirstFocus");
27728         this.editorcore.onFirstFocus();
27729          for (var i =0; i < this.toolbars.length;i++) {
27730             this.toolbars[i].onFirstFocus();
27731         }
27732         
27733     },
27734     
27735     // private
27736     syncValue : function()
27737     {
27738         this.editorcore.syncValue();
27739     },
27740     
27741     pushValue : function()
27742     {
27743         this.editorcore.pushValue();
27744     },
27745     
27746     setStylesheets : function(stylesheets)
27747     {
27748         this.editorcore.setStylesheets(stylesheets);
27749     },
27750     
27751     removeStylesheets : function()
27752     {
27753         this.editorcore.removeStylesheets();
27754     }
27755      
27756     
27757     // hide stuff that is not compatible
27758     /**
27759      * @event blur
27760      * @hide
27761      */
27762     /**
27763      * @event change
27764      * @hide
27765      */
27766     /**
27767      * @event focus
27768      * @hide
27769      */
27770     /**
27771      * @event specialkey
27772      * @hide
27773      */
27774     /**
27775      * @cfg {String} fieldClass @hide
27776      */
27777     /**
27778      * @cfg {String} focusClass @hide
27779      */
27780     /**
27781      * @cfg {String} autoCreate @hide
27782      */
27783     /**
27784      * @cfg {String} inputType @hide
27785      */
27786     /**
27787      * @cfg {String} invalidClass @hide
27788      */
27789     /**
27790      * @cfg {String} invalidText @hide
27791      */
27792     /**
27793      * @cfg {String} msgFx @hide
27794      */
27795     /**
27796      * @cfg {String} validateOnBlur @hide
27797      */
27798 });
27799  
27800     /*
27801  * Based on
27802  * Ext JS Library 1.1.1
27803  * Copyright(c) 2006-2007, Ext JS, LLC.
27804  *  
27805  
27806  */
27807
27808 /**
27809  * @class Roo.form.HtmlEditor.ToolbarStandard
27810  * Basic Toolbar
27811
27812  * Usage:
27813  *
27814  new Roo.form.HtmlEditor({
27815     ....
27816     toolbars : [
27817         new Roo.form.HtmlEditorToolbar1({
27818             disable : { fonts: 1 , format: 1, ..., ... , ...],
27819             btns : [ .... ]
27820         })
27821     }
27822      
27823  * 
27824  * @cfg {Object} disable List of elements to disable..
27825  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27826  * 
27827  * 
27828  * NEEDS Extra CSS? 
27829  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27830  */
27831  
27832 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27833 {
27834     
27835     Roo.apply(this, config);
27836     
27837     // default disabled, based on 'good practice'..
27838     this.disable = this.disable || {};
27839     Roo.applyIf(this.disable, {
27840         fontSize : true,
27841         colors : true,
27842         specialElements : true
27843     });
27844     
27845     
27846     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27847     // dont call parent... till later.
27848 }
27849
27850 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27851     
27852     tb: false,
27853     
27854     rendered: false,
27855     
27856     editor : false,
27857     editorcore : false,
27858     /**
27859      * @cfg {Object} disable  List of toolbar elements to disable
27860          
27861      */
27862     disable : false,
27863     
27864     
27865      /**
27866      * @cfg {String} createLinkText The default text for the create link prompt
27867      */
27868     createLinkText : 'Please enter the URL for the link:',
27869     /**
27870      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27871      */
27872     defaultLinkValue : 'http:/'+'/',
27873    
27874     
27875       /**
27876      * @cfg {Array} fontFamilies An array of available font families
27877      */
27878     fontFamilies : [
27879         'Arial',
27880         'Courier New',
27881         'Tahoma',
27882         'Times New Roman',
27883         'Verdana'
27884     ],
27885     
27886     specialChars : [
27887            "&#169;",
27888           "&#174;",     
27889           "&#8482;",    
27890           "&#163;" ,    
27891          // "&#8212;",    
27892           "&#8230;",    
27893           "&#247;" ,    
27894         //  "&#225;" ,     ?? a acute?
27895            "&#8364;"    , //Euro
27896        //   "&#8220;"    ,
27897         //  "&#8221;"    ,
27898         //  "&#8226;"    ,
27899           "&#176;"  //   , // degrees
27900
27901          // "&#233;"     , // e ecute
27902          // "&#250;"     , // u ecute?
27903     ],
27904     
27905     specialElements : [
27906         {
27907             text: "Insert Table",
27908             xtype: 'MenuItem',
27909             xns : Roo.Menu,
27910             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27911                 
27912         },
27913         {    
27914             text: "Insert Image",
27915             xtype: 'MenuItem',
27916             xns : Roo.Menu,
27917             ihtml : '<img src="about:blank"/>'
27918             
27919         }
27920         
27921          
27922     ],
27923     
27924     
27925     inputElements : [ 
27926             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27927             "input:submit", "input:button", "select", "textarea", "label" ],
27928     formats : [
27929         ["p"] ,  
27930         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27931         ["pre"],[ "code"], 
27932         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27933         ['div'],['span'],
27934         ['sup'],['sub']
27935     ],
27936     
27937     cleanStyles : [
27938         "font-size"
27939     ],
27940      /**
27941      * @cfg {String} defaultFont default font to use.
27942      */
27943     defaultFont: 'tahoma',
27944    
27945     fontSelect : false,
27946     
27947     
27948     formatCombo : false,
27949     
27950     init : function(editor)
27951     {
27952         this.editor = editor;
27953         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27954         var editorcore = this.editorcore;
27955         
27956         var _t = this;
27957         
27958         var fid = editorcore.frameId;
27959         var etb = this;
27960         function btn(id, toggle, handler){
27961             var xid = fid + '-'+ id ;
27962             return {
27963                 id : xid,
27964                 cmd : id,
27965                 cls : 'x-btn-icon x-edit-'+id,
27966                 enableToggle:toggle !== false,
27967                 scope: _t, // was editor...
27968                 handler:handler||_t.relayBtnCmd,
27969                 clickEvent:'mousedown',
27970                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27971                 tabIndex:-1
27972             };
27973         }
27974         
27975         
27976         
27977         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27978         this.tb = tb;
27979          // stop form submits
27980         tb.el.on('click', function(e){
27981             e.preventDefault(); // what does this do?
27982         });
27983
27984         if(!this.disable.font) { // && !Roo.isSafari){
27985             /* why no safari for fonts 
27986             editor.fontSelect = tb.el.createChild({
27987                 tag:'select',
27988                 tabIndex: -1,
27989                 cls:'x-font-select',
27990                 html: this.createFontOptions()
27991             });
27992             
27993             editor.fontSelect.on('change', function(){
27994                 var font = editor.fontSelect.dom.value;
27995                 editor.relayCmd('fontname', font);
27996                 editor.deferFocus();
27997             }, editor);
27998             
27999             tb.add(
28000                 editor.fontSelect.dom,
28001                 '-'
28002             );
28003             */
28004             
28005         };
28006         if(!this.disable.formats){
28007             this.formatCombo = new Roo.form.ComboBox({
28008                 store: new Roo.data.SimpleStore({
28009                     id : 'tag',
28010                     fields: ['tag'],
28011                     data : this.formats // from states.js
28012                 }),
28013                 blockFocus : true,
28014                 name : '',
28015                 //autoCreate : {tag: "div",  size: "20"},
28016                 displayField:'tag',
28017                 typeAhead: false,
28018                 mode: 'local',
28019                 editable : false,
28020                 triggerAction: 'all',
28021                 emptyText:'Add tag',
28022                 selectOnFocus:true,
28023                 width:135,
28024                 listeners : {
28025                     'select': function(c, r, i) {
28026                         editorcore.insertTag(r.get('tag'));
28027                         editor.focus();
28028                     }
28029                 }
28030
28031             });
28032             tb.addField(this.formatCombo);
28033             
28034         }
28035         
28036         if(!this.disable.format){
28037             tb.add(
28038                 btn('bold'),
28039                 btn('italic'),
28040                 btn('underline'),
28041                 btn('strikethrough')
28042             );
28043         };
28044         if(!this.disable.fontSize){
28045             tb.add(
28046                 '-',
28047                 
28048                 
28049                 btn('increasefontsize', false, editorcore.adjustFont),
28050                 btn('decreasefontsize', false, editorcore.adjustFont)
28051             );
28052         };
28053         
28054         
28055         if(!this.disable.colors){
28056             tb.add(
28057                 '-', {
28058                     id:editorcore.frameId +'-forecolor',
28059                     cls:'x-btn-icon x-edit-forecolor',
28060                     clickEvent:'mousedown',
28061                     tooltip: this.buttonTips['forecolor'] || undefined,
28062                     tabIndex:-1,
28063                     menu : new Roo.menu.ColorMenu({
28064                         allowReselect: true,
28065                         focus: Roo.emptyFn,
28066                         value:'000000',
28067                         plain:true,
28068                         selectHandler: function(cp, color){
28069                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
28070                             editor.deferFocus();
28071                         },
28072                         scope: editorcore,
28073                         clickEvent:'mousedown'
28074                     })
28075                 }, {
28076                     id:editorcore.frameId +'backcolor',
28077                     cls:'x-btn-icon x-edit-backcolor',
28078                     clickEvent:'mousedown',
28079                     tooltip: this.buttonTips['backcolor'] || undefined,
28080                     tabIndex:-1,
28081                     menu : new Roo.menu.ColorMenu({
28082                         focus: Roo.emptyFn,
28083                         value:'FFFFFF',
28084                         plain:true,
28085                         allowReselect: true,
28086                         selectHandler: function(cp, color){
28087                             if(Roo.isGecko){
28088                                 editorcore.execCmd('useCSS', false);
28089                                 editorcore.execCmd('hilitecolor', color);
28090                                 editorcore.execCmd('useCSS', true);
28091                                 editor.deferFocus();
28092                             }else{
28093                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
28094                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
28095                                 editor.deferFocus();
28096                             }
28097                         },
28098                         scope:editorcore,
28099                         clickEvent:'mousedown'
28100                     })
28101                 }
28102             );
28103         };
28104         // now add all the items...
28105         
28106
28107         if(!this.disable.alignments){
28108             tb.add(
28109                 '-',
28110                 btn('justifyleft'),
28111                 btn('justifycenter'),
28112                 btn('justifyright')
28113             );
28114         };
28115
28116         //if(!Roo.isSafari){
28117             if(!this.disable.links){
28118                 tb.add(
28119                     '-',
28120                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
28121                 );
28122             };
28123
28124             if(!this.disable.lists){
28125                 tb.add(
28126                     '-',
28127                     btn('insertorderedlist'),
28128                     btn('insertunorderedlist')
28129                 );
28130             }
28131             if(!this.disable.sourceEdit){
28132                 tb.add(
28133                     '-',
28134                     btn('sourceedit', true, function(btn){
28135                         this.toggleSourceEdit(btn.pressed);
28136                     })
28137                 );
28138             }
28139         //}
28140         
28141         var smenu = { };
28142         // special menu.. - needs to be tidied up..
28143         if (!this.disable.special) {
28144             smenu = {
28145                 text: "&#169;",
28146                 cls: 'x-edit-none',
28147                 
28148                 menu : {
28149                     items : []
28150                 }
28151             };
28152             for (var i =0; i < this.specialChars.length; i++) {
28153                 smenu.menu.items.push({
28154                     
28155                     html: this.specialChars[i],
28156                     handler: function(a,b) {
28157                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
28158                         //editor.insertAtCursor(a.html);
28159                         
28160                     },
28161                     tabIndex:-1
28162                 });
28163             }
28164             
28165             
28166             tb.add(smenu);
28167             
28168             
28169         }
28170         
28171         var cmenu = { };
28172         if (!this.disable.cleanStyles) {
28173             cmenu = {
28174                 cls: 'x-btn-icon x-btn-clear',
28175                 
28176                 menu : {
28177                     items : []
28178                 }
28179             };
28180             for (var i =0; i < this.cleanStyles.length; i++) {
28181                 cmenu.menu.items.push({
28182                     actiontype : this.cleanStyles[i],
28183                     html: 'Remove ' + this.cleanStyles[i],
28184                     handler: function(a,b) {
28185 //                        Roo.log(a);
28186 //                        Roo.log(b);
28187                         var c = Roo.get(editorcore.doc.body);
28188                         c.select('[style]').each(function(s) {
28189                             s.dom.style.removeProperty(a.actiontype);
28190                         });
28191                         editorcore.syncValue();
28192                     },
28193                     tabIndex:-1
28194                 });
28195             }
28196             cmenu.menu.items.push({
28197                 actiontype : 'tablewidths',
28198                 html: 'Remove Table Widths',
28199                 handler: function(a,b) {
28200                     editorcore.cleanTableWidths();
28201                     editorcore.syncValue();
28202                 },
28203                 tabIndex:-1
28204             });
28205             cmenu.menu.items.push({
28206                 actiontype : 'word',
28207                 html: 'Remove MS Word Formating',
28208                 handler: function(a,b) {
28209                     editorcore.cleanWord();
28210                     editorcore.syncValue();
28211                 },
28212                 tabIndex:-1
28213             });
28214             
28215             cmenu.menu.items.push({
28216                 actiontype : 'all',
28217                 html: 'Remove All Styles',
28218                 handler: function(a,b) {
28219                     
28220                     var c = Roo.get(editorcore.doc.body);
28221                     c.select('[style]').each(function(s) {
28222                         s.dom.removeAttribute('style');
28223                     });
28224                     editorcore.syncValue();
28225                 },
28226                 tabIndex:-1
28227             });
28228             
28229             cmenu.menu.items.push({
28230                 actiontype : 'all',
28231                 html: 'Remove All CSS Classes',
28232                 handler: function(a,b) {
28233                     
28234                     var c = Roo.get(editorcore.doc.body);
28235                     c.select('[class]').each(function(s) {
28236                         s.dom.removeAttribute('class');
28237                     });
28238                     editorcore.cleanWord();
28239                     editorcore.syncValue();
28240                 },
28241                 tabIndex:-1
28242             });
28243             
28244              cmenu.menu.items.push({
28245                 actiontype : 'tidy',
28246                 html: 'Tidy HTML Source',
28247                 handler: function(a,b) {
28248                     new Roo.htmleditor.Tidy(editorcore.doc.body);
28249                     editorcore.syncValue();
28250                 },
28251                 tabIndex:-1
28252             });
28253             
28254             
28255             tb.add(cmenu);
28256         }
28257          
28258         if (!this.disable.specialElements) {
28259             var semenu = {
28260                 text: "Other;",
28261                 cls: 'x-edit-none',
28262                 menu : {
28263                     items : []
28264                 }
28265             };
28266             for (var i =0; i < this.specialElements.length; i++) {
28267                 semenu.menu.items.push(
28268                     Roo.apply({ 
28269                         handler: function(a,b) {
28270                             editor.insertAtCursor(this.ihtml);
28271                         }
28272                     }, this.specialElements[i])
28273                 );
28274                     
28275             }
28276             
28277             tb.add(semenu);
28278             
28279             
28280         }
28281          
28282         
28283         if (this.btns) {
28284             for(var i =0; i< this.btns.length;i++) {
28285                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
28286                 b.cls =  'x-edit-none';
28287                 
28288                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
28289                     b.cls += ' x-init-enable';
28290                 }
28291                 
28292                 b.scope = editorcore;
28293                 tb.add(b);
28294             }
28295         
28296         }
28297         
28298         
28299         
28300         // disable everything...
28301         
28302         this.tb.items.each(function(item){
28303             
28304            if(
28305                 item.id != editorcore.frameId+ '-sourceedit' && 
28306                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
28307             ){
28308                 
28309                 item.disable();
28310             }
28311         });
28312         this.rendered = true;
28313         
28314         // the all the btns;
28315         editor.on('editorevent', this.updateToolbar, this);
28316         // other toolbars need to implement this..
28317         //editor.on('editmodechange', this.updateToolbar, this);
28318     },
28319     
28320     
28321     relayBtnCmd : function(btn) {
28322         this.editorcore.relayCmd(btn.cmd);
28323     },
28324     // private used internally
28325     createLink : function(){
28326         //Roo.log("create link?");
28327         var ec = this.editorcore;
28328         var ar = ec.getAllAncestors();
28329         var n = false;
28330         for(var i = 0;i< ar.length;i++) {
28331             if (ar[i] && ar[i].nodeName == 'A') {
28332                 n = ar[i];
28333                 break;
28334             }
28335         }
28336         
28337         (function() {
28338             
28339             Roo.MessageBox.show({
28340                 title : "Add / Edit Link URL",
28341                 msg : "Enter the url for the link",
28342                 buttons: Roo.MessageBox.OKCANCEL,
28343                 fn: function(btn, url){
28344                     if (btn != 'ok') {
28345                         return;
28346                     }
28347                     if(url && url != 'http:/'+'/'){
28348                         if (n) {
28349                             n.setAttribute('href', url);
28350                         } else {
28351                             ec.relayCmd('createlink', url);
28352                         }
28353                     }
28354                 },
28355                 minWidth:250,
28356                 prompt:true,
28357                 //multiline: multiline,
28358                 modal : true,
28359                 value :  n  ? n.getAttribute('href') : '' 
28360             });
28361             
28362              
28363         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
28364         
28365     },
28366
28367     
28368     /**
28369      * Protected method that will not generally be called directly. It triggers
28370      * a toolbar update by reading the markup state of the current selection in the editor.
28371      */
28372     updateToolbar: function(){
28373
28374         if(!this.editorcore.activated){
28375             this.editor.onFirstFocus();
28376             return;
28377         }
28378
28379         var btns = this.tb.items.map, 
28380             doc = this.editorcore.doc,
28381             frameId = this.editorcore.frameId;
28382
28383         if(!this.disable.font && !Roo.isSafari){
28384             /*
28385             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
28386             if(name != this.fontSelect.dom.value){
28387                 this.fontSelect.dom.value = name;
28388             }
28389             */
28390         }
28391         if(!this.disable.format){
28392             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
28393             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
28394             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
28395             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
28396         }
28397         if(!this.disable.alignments){
28398             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
28399             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
28400             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
28401         }
28402         if(!Roo.isSafari && !this.disable.lists){
28403             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
28404             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
28405         }
28406         
28407         var ans = this.editorcore.getAllAncestors();
28408         if (this.formatCombo) {
28409             
28410             
28411             var store = this.formatCombo.store;
28412             this.formatCombo.setValue("");
28413             for (var i =0; i < ans.length;i++) {
28414                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28415                     // select it..
28416                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28417                     break;
28418                 }
28419             }
28420         }
28421         
28422         
28423         
28424         // hides menus... - so this cant be on a menu...
28425         Roo.menu.MenuMgr.hideAll();
28426
28427         //this.editorsyncValue();
28428     },
28429    
28430     
28431     createFontOptions : function(){
28432         var buf = [], fs = this.fontFamilies, ff, lc;
28433         
28434         
28435         
28436         for(var i = 0, len = fs.length; i< len; i++){
28437             ff = fs[i];
28438             lc = ff.toLowerCase();
28439             buf.push(
28440                 '<option value="',lc,'" style="font-family:',ff,';"',
28441                     (this.defaultFont == lc ? ' selected="true">' : '>'),
28442                     ff,
28443                 '</option>'
28444             );
28445         }
28446         return buf.join('');
28447     },
28448     
28449     toggleSourceEdit : function(sourceEditMode){
28450         
28451         Roo.log("toolbar toogle");
28452         if(sourceEditMode === undefined){
28453             sourceEditMode = !this.sourceEditMode;
28454         }
28455         this.sourceEditMode = sourceEditMode === true;
28456         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
28457         // just toggle the button?
28458         if(btn.pressed !== this.sourceEditMode){
28459             btn.toggle(this.sourceEditMode);
28460             return;
28461         }
28462         
28463         if(sourceEditMode){
28464             Roo.log("disabling buttons");
28465             this.tb.items.each(function(item){
28466                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
28467                     item.disable();
28468                 }
28469             });
28470           
28471         }else{
28472             Roo.log("enabling buttons");
28473             if(this.editorcore.initialized){
28474                 this.tb.items.each(function(item){
28475                     item.enable();
28476                 });
28477                 // initialize 'blocks'
28478                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
28479                     Roo.htmleditor.Block.factory(e).updateElement(e);
28480                 },this);
28481             
28482             }
28483             
28484         }
28485         Roo.log("calling toggole on editor");
28486         // tell the editor that it's been pressed..
28487         this.editor.toggleSourceEdit(sourceEditMode);
28488        
28489     },
28490      /**
28491      * Object collection of toolbar tooltips for the buttons in the editor. The key
28492      * is the command id associated with that button and the value is a valid QuickTips object.
28493      * For example:
28494 <pre><code>
28495 {
28496     bold : {
28497         title: 'Bold (Ctrl+B)',
28498         text: 'Make the selected text bold.',
28499         cls: 'x-html-editor-tip'
28500     },
28501     italic : {
28502         title: 'Italic (Ctrl+I)',
28503         text: 'Make the selected text italic.',
28504         cls: 'x-html-editor-tip'
28505     },
28506     ...
28507 </code></pre>
28508     * @type Object
28509      */
28510     buttonTips : {
28511         bold : {
28512             title: 'Bold (Ctrl+B)',
28513             text: 'Make the selected text bold.',
28514             cls: 'x-html-editor-tip'
28515         },
28516         italic : {
28517             title: 'Italic (Ctrl+I)',
28518             text: 'Make the selected text italic.',
28519             cls: 'x-html-editor-tip'
28520         },
28521         underline : {
28522             title: 'Underline (Ctrl+U)',
28523             text: 'Underline the selected text.',
28524             cls: 'x-html-editor-tip'
28525         },
28526         strikethrough : {
28527             title: 'Strikethrough',
28528             text: 'Strikethrough the selected text.',
28529             cls: 'x-html-editor-tip'
28530         },
28531         increasefontsize : {
28532             title: 'Grow Text',
28533             text: 'Increase the font size.',
28534             cls: 'x-html-editor-tip'
28535         },
28536         decreasefontsize : {
28537             title: 'Shrink Text',
28538             text: 'Decrease the font size.',
28539             cls: 'x-html-editor-tip'
28540         },
28541         backcolor : {
28542             title: 'Text Highlight Color',
28543             text: 'Change the background color of the selected text.',
28544             cls: 'x-html-editor-tip'
28545         },
28546         forecolor : {
28547             title: 'Font Color',
28548             text: 'Change the color of the selected text.',
28549             cls: 'x-html-editor-tip'
28550         },
28551         justifyleft : {
28552             title: 'Align Text Left',
28553             text: 'Align text to the left.',
28554             cls: 'x-html-editor-tip'
28555         },
28556         justifycenter : {
28557             title: 'Center Text',
28558             text: 'Center text in the editor.',
28559             cls: 'x-html-editor-tip'
28560         },
28561         justifyright : {
28562             title: 'Align Text Right',
28563             text: 'Align text to the right.',
28564             cls: 'x-html-editor-tip'
28565         },
28566         insertunorderedlist : {
28567             title: 'Bullet List',
28568             text: 'Start a bulleted list.',
28569             cls: 'x-html-editor-tip'
28570         },
28571         insertorderedlist : {
28572             title: 'Numbered List',
28573             text: 'Start a numbered list.',
28574             cls: 'x-html-editor-tip'
28575         },
28576         createlink : {
28577             title: 'Hyperlink',
28578             text: 'Make the selected text a hyperlink.',
28579             cls: 'x-html-editor-tip'
28580         },
28581         sourceedit : {
28582             title: 'Source Edit',
28583             text: 'Switch to source editing mode.',
28584             cls: 'x-html-editor-tip'
28585         }
28586     },
28587     // private
28588     onDestroy : function(){
28589         if(this.rendered){
28590             
28591             this.tb.items.each(function(item){
28592                 if(item.menu){
28593                     item.menu.removeAll();
28594                     if(item.menu.el){
28595                         item.menu.el.destroy();
28596                     }
28597                 }
28598                 item.destroy();
28599             });
28600              
28601         }
28602     },
28603     onFirstFocus: function() {
28604         this.tb.items.each(function(item){
28605            item.enable();
28606         });
28607     }
28608 };
28609
28610
28611
28612
28613 // <script type="text/javascript">
28614 /*
28615  * Based on
28616  * Ext JS Library 1.1.1
28617  * Copyright(c) 2006-2007, Ext JS, LLC.
28618  *  
28619  
28620  */
28621
28622  
28623 /**
28624  * @class Roo.form.HtmlEditor.ToolbarContext
28625  * Context Toolbar
28626  * 
28627  * Usage:
28628  *
28629  new Roo.form.HtmlEditor({
28630     ....
28631     toolbars : [
28632         { xtype: 'ToolbarStandard', styles : {} }
28633         { xtype: 'ToolbarContext', disable : {} }
28634     ]
28635 })
28636
28637      
28638  * 
28639  * @config : {Object} disable List of elements to disable.. (not done yet.)
28640  * @config : {Object} styles  Map of styles available.
28641  * 
28642  */
28643
28644 Roo.form.HtmlEditor.ToolbarContext = function(config)
28645 {
28646     
28647     Roo.apply(this, config);
28648     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28649     // dont call parent... till later.
28650     this.styles = this.styles || {};
28651 }
28652
28653  
28654
28655 Roo.form.HtmlEditor.ToolbarContext.types = {
28656     'IMG' : [
28657         {
28658             name : 'width',
28659             title: "Width",
28660             width: 40
28661         },
28662         {
28663             name : 'height',
28664             title: "Height",
28665             width: 40
28666         },
28667         {
28668             name : 'align',
28669             title: "Align",
28670             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28671             width : 80
28672             
28673         },
28674         {
28675             name : 'border',
28676             title: "Border",
28677             width: 40
28678         },
28679         {
28680             name : 'alt',
28681             title: "Alt",
28682             width: 120
28683         },
28684         {
28685             name : 'src',
28686             title: "Src",
28687             width: 220
28688         }
28689         
28690     ],
28691     
28692     'FIGURE' : [
28693         {
28694             name : 'align',
28695             title: "Align",
28696             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28697             width : 80  
28698         }
28699     ],
28700     'A' : [
28701         {
28702             name : 'name',
28703             title: "Name",
28704             width: 50
28705         },
28706         {
28707             name : 'target',
28708             title: "Target",
28709             width: 120
28710         },
28711         {
28712             name : 'href',
28713             title: "Href",
28714             width: 220
28715         } // border?
28716         
28717     ],
28718     
28719     'INPUT' : [
28720         {
28721             name : 'name',
28722             title: "name",
28723             width: 120
28724         },
28725         {
28726             name : 'value',
28727             title: "Value",
28728             width: 120
28729         },
28730         {
28731             name : 'width',
28732             title: "Width",
28733             width: 40
28734         }
28735     ],
28736     'LABEL' : [
28737          {
28738             name : 'for',
28739             title: "For",
28740             width: 120
28741         }
28742     ],
28743     'TEXTAREA' : [
28744         {
28745             name : 'name',
28746             title: "name",
28747             width: 120
28748         },
28749         {
28750             name : 'rows',
28751             title: "Rows",
28752             width: 20
28753         },
28754         {
28755             name : 'cols',
28756             title: "Cols",
28757             width: 20
28758         }
28759     ],
28760     'SELECT' : [
28761         {
28762             name : 'name',
28763             title: "name",
28764             width: 120
28765         },
28766         {
28767             name : 'selectoptions',
28768             title: "Options",
28769             width: 200
28770         }
28771     ],
28772     
28773     // should we really allow this??
28774     // should this just be 
28775     'BODY' : [
28776         
28777         {
28778             name : 'title',
28779             title: "Title",
28780             width: 200,
28781             disabled : true
28782         }
28783     ],
28784  
28785     '*' : [
28786         // empty.
28787     ]
28788
28789 };
28790
28791 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28792 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28793
28794 Roo.form.HtmlEditor.ToolbarContext.options = {
28795         'font-family'  : [ 
28796                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28797                 [ 'Courier New', 'Courier New'],
28798                 [ 'Tahoma', 'Tahoma'],
28799                 [ 'Times New Roman,serif', 'Times'],
28800                 [ 'Verdana','Verdana' ]
28801         ]
28802 };
28803
28804 // fixme - these need to be configurable..
28805  
28806
28807 //Roo.form.HtmlEditor.ToolbarContext.types
28808
28809
28810 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28811     
28812     tb: false,
28813     
28814     rendered: false,
28815     
28816     editor : false,
28817     editorcore : false,
28818     /**
28819      * @cfg {Object} disable  List of toolbar elements to disable
28820          
28821      */
28822     disable : false,
28823     /**
28824      * @cfg {Object} styles List of styles 
28825      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28826      *
28827      * These must be defined in the page, so they get rendered correctly..
28828      * .headline { }
28829      * TD.underline { }
28830      * 
28831      */
28832     styles : false,
28833     
28834     options: false,
28835     
28836     toolbars : false,
28837     
28838     init : function(editor)
28839     {
28840         this.editor = editor;
28841         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28842         var editorcore = this.editorcore;
28843         
28844         var fid = editorcore.frameId;
28845         var etb = this;
28846         function btn(id, toggle, handler){
28847             var xid = fid + '-'+ id ;
28848             return {
28849                 id : xid,
28850                 cmd : id,
28851                 cls : 'x-btn-icon x-edit-'+id,
28852                 enableToggle:toggle !== false,
28853                 scope: editorcore, // was editor...
28854                 handler:handler||editorcore.relayBtnCmd,
28855                 clickEvent:'mousedown',
28856                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28857                 tabIndex:-1
28858             };
28859         }
28860         // create a new element.
28861         var wdiv = editor.wrap.createChild({
28862                 tag: 'div'
28863             }, editor.wrap.dom.firstChild.nextSibling, true);
28864         
28865         // can we do this more than once??
28866         
28867          // stop form submits
28868       
28869  
28870         // disable everything...
28871         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28872         this.toolbars = {};
28873         // block toolbars are built in updateToolbar when needed.
28874         for (var i in  ty) {
28875             
28876             this.toolbars[i] = this.buildToolbar(ty[i],i);
28877         }
28878         this.tb = this.toolbars.BODY;
28879         this.tb.el.show();
28880         this.buildFooter();
28881         this.footer.show();
28882         editor.on('hide', function( ) { this.footer.hide() }, this);
28883         editor.on('show', function( ) { this.footer.show() }, this);
28884         
28885          
28886         this.rendered = true;
28887         
28888         // the all the btns;
28889         editor.on('editorevent', this.updateToolbar, this);
28890         // other toolbars need to implement this..
28891         //editor.on('editmodechange', this.updateToolbar, this);
28892     },
28893     
28894     
28895     
28896     /**
28897      * Protected method that will not generally be called directly. It triggers
28898      * a toolbar update by reading the markup state of the current selection in the editor.
28899      *
28900      * Note you can force an update by calling on('editorevent', scope, false)
28901      */
28902     updateToolbar: function(editor ,ev, sel)
28903     {
28904         
28905         if (ev) {
28906             ev.stopEvent(); // se if we can stop this looping with mutiple events.
28907         }
28908         
28909         //Roo.log(ev);
28910         // capture mouse up - this is handy for selecting images..
28911         // perhaps should go somewhere else...
28912         if(!this.editorcore.activated){
28913              this.editor.onFirstFocus();
28914             return;
28915         }
28916         //Roo.log(ev ? ev.target : 'NOTARGET');
28917         
28918         
28919         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28920         // selectNode - might want to handle IE?
28921         
28922         
28923         
28924         if (ev &&
28925             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28926             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28927             // they have click on an image...
28928             // let's see if we can change the selection...
28929             sel = ev.target;
28930             
28931             // this triggers looping?
28932             //this.editorcore.selectNode(sel);
28933              
28934         }
28935         
28936         // this forces an id..
28937         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28938              e.classList.remove('roo-ed-selection');
28939         });
28940         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28941         //Roo.get(node).addClass('roo-ed-selection');
28942       
28943         //var updateFooter = sel ? false : true; 
28944         
28945         
28946         var ans = this.editorcore.getAllAncestors();
28947         
28948         // pick
28949         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28950         
28951         if (!sel) { 
28952             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28953             sel = sel ? sel : this.editorcore.doc.body;
28954             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28955             
28956         }
28957         
28958         var tn = sel.tagName.toUpperCase();
28959         var lastSel = this.tb.selectedNode;
28960         this.tb.selectedNode = sel;
28961         var left_label = tn;
28962         
28963         // ok see if we are editing a block?
28964         
28965         var db = false;
28966         // you are not actually selecting the block.
28967         if (sel && sel.hasAttribute('data-block')) {
28968             db = sel;
28969         } else if (sel && sel.closest('[data-block]')) {
28970             
28971             db = sel.closest('[data-block]');
28972             //var cepar = sel.closest('[contenteditable=true]');
28973             //if (db && cepar && cepar.tagName != 'BODY') {
28974             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28975             //}   
28976         }
28977         
28978         
28979         var block = false;
28980         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28981         if (db && this.editorcore.enableBlocks) {
28982             block = Roo.htmleditor.Block.factory(db);
28983             
28984             
28985             if (block) {
28986                  db.className = (
28987                         db.classList.length > 0  ? db.className + ' ' : ''
28988                     )  + 'roo-ed-selection';
28989                  
28990                  // since we removed it earlier... its not there..
28991                 tn = 'BLOCK.' + db.getAttribute('data-block');
28992                 
28993                 //this.editorcore.selectNode(db);
28994                 if (typeof(this.toolbars[tn]) == 'undefined') {
28995                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
28996                 }
28997                 this.toolbars[tn].selectedNode = db;
28998                 left_label = block.friendly_name;
28999                 ans = this.editorcore.getAllAncestors();
29000             }
29001             
29002                 
29003             
29004         }
29005         
29006         
29007         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
29008             return; // no change?
29009         }
29010         
29011         
29012           
29013         this.tb.el.hide();
29014         ///console.log("show: " + tn);
29015         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
29016         
29017         this.tb.el.show();
29018         // update name
29019         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
29020         
29021         
29022         // update attributes
29023         if (block && this.tb.fields) {
29024              
29025             this.tb.fields.each(function(e) {
29026                 e.setValue(block[e.name]);
29027             });
29028             
29029             
29030         } else  if (this.tb.fields && this.tb.selectedNode) {
29031             this.tb.fields.each( function(e) {
29032                 if (e.stylename) {
29033                     e.setValue(this.tb.selectedNode.style[e.stylename]);
29034                     return;
29035                 } 
29036                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
29037             }, this);
29038             this.updateToolbarStyles(this.tb.selectedNode);  
29039         }
29040         
29041         
29042        
29043         Roo.menu.MenuMgr.hideAll();
29044
29045         
29046         
29047     
29048         // update the footer
29049         //
29050         this.updateFooter(ans);
29051              
29052     },
29053     
29054     updateToolbarStyles : function(sel)
29055     {
29056         var hasStyles = false;
29057         for(var i in this.styles) {
29058             hasStyles = true;
29059             break;
29060         }
29061         
29062         // update styles
29063         if (hasStyles && this.tb.hasStyles) { 
29064             var st = this.tb.fields.item(0);
29065             
29066             st.store.removeAll();
29067             var cn = sel.className.split(/\s+/);
29068             
29069             var avs = [];
29070             if (this.styles['*']) {
29071                 
29072                 Roo.each(this.styles['*'], function(v) {
29073                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
29074                 });
29075             }
29076             if (this.styles[tn]) { 
29077                 Roo.each(this.styles[tn], function(v) {
29078                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
29079                 });
29080             }
29081             
29082             st.store.loadData(avs);
29083             st.collapse();
29084             st.setValue(cn);
29085         }
29086     },
29087     
29088      
29089     updateFooter : function(ans)
29090     {
29091         var html = '';
29092         if (ans === false) {
29093             this.footDisp.dom.innerHTML = '';
29094             return;
29095         }
29096         
29097         this.footerEls = ans.reverse();
29098         Roo.each(this.footerEls, function(a,i) {
29099             if (!a) { return; }
29100             html += html.length ? ' &gt; '  :  '';
29101             
29102             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
29103             
29104         });
29105        
29106         // 
29107         var sz = this.footDisp.up('td').getSize();
29108         this.footDisp.dom.style.width = (sz.width -10) + 'px';
29109         this.footDisp.dom.style.marginLeft = '5px';
29110         
29111         this.footDisp.dom.style.overflow = 'hidden';
29112         
29113         this.footDisp.dom.innerHTML = html;
29114             
29115         
29116     },
29117    
29118        
29119     // private
29120     onDestroy : function(){
29121         if(this.rendered){
29122             
29123             this.tb.items.each(function(item){
29124                 if(item.menu){
29125                     item.menu.removeAll();
29126                     if(item.menu.el){
29127                         item.menu.el.destroy();
29128                     }
29129                 }
29130                 item.destroy();
29131             });
29132              
29133         }
29134     },
29135     onFirstFocus: function() {
29136         // need to do this for all the toolbars..
29137         this.tb.items.each(function(item){
29138            item.enable();
29139         });
29140     },
29141     buildToolbar: function(tlist, nm, friendly_name, block)
29142     {
29143         var editor = this.editor;
29144         var editorcore = this.editorcore;
29145          // create a new element.
29146         var wdiv = editor.wrap.createChild({
29147                 tag: 'div'
29148             }, editor.wrap.dom.firstChild.nextSibling, true);
29149         
29150        
29151         var tb = new Roo.Toolbar(wdiv);
29152         ///this.tb = tb; // << this sets the active toolbar..
29153         if (tlist === false && block) {
29154             tlist = block.contextMenu(this);
29155         }
29156         
29157         tb.hasStyles = false;
29158         tb.name = nm;
29159         
29160         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
29161         
29162         var styles = Array.from(this.styles);
29163         
29164         
29165         // styles...
29166         if (styles && styles.length) {
29167             tb.hasStyles = true;
29168             // this needs a multi-select checkbox...
29169             tb.addField( new Roo.form.ComboBox({
29170                 store: new Roo.data.SimpleStore({
29171                     id : 'val',
29172                     fields: ['val', 'selected'],
29173                     data : [] 
29174                 }),
29175                 name : '-roo-edit-className',
29176                 attrname : 'className',
29177                 displayField: 'val',
29178                 typeAhead: false,
29179                 mode: 'local',
29180                 editable : false,
29181                 triggerAction: 'all',
29182                 emptyText:'Select Style',
29183                 selectOnFocus:true,
29184                 width: 130,
29185                 listeners : {
29186                     'select': function(c, r, i) {
29187                         // initial support only for on class per el..
29188                         tb.selectedNode.className =  r ? r.get('val') : '';
29189                         editorcore.syncValue();
29190                     }
29191                 }
29192     
29193             }));
29194         }
29195         
29196         var tbc = Roo.form.HtmlEditor.ToolbarContext;
29197         
29198         
29199         for (var i = 0; i < tlist.length; i++) {
29200             
29201             // newer versions will use xtype cfg to create menus.
29202             if (typeof(tlist[i].xtype) != 'undefined') {
29203                 
29204                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
29205                 
29206                 
29207                 continue;
29208             }
29209             
29210             var item = tlist[i];
29211             tb.add(item.title + ":&nbsp;");
29212             
29213             
29214             //optname == used so you can configure the options available..
29215             var opts = item.opts ? item.opts : false;
29216             if (item.optname) { // use the b
29217                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
29218            
29219             }
29220             
29221             if (opts) {
29222                 // opts == pulldown..
29223                 tb.addField( new Roo.form.ComboBox({
29224                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
29225                         id : 'val',
29226                         fields: ['val', 'display'],
29227                         data : opts  
29228                     }),
29229                     name : '-roo-edit-' + tlist[i].name,
29230                     
29231                     attrname : tlist[i].name,
29232                     stylename : item.style ? item.style : false,
29233                     
29234                     displayField: item.displayField ? item.displayField : 'val',
29235                     valueField :  'val',
29236                     typeAhead: false,
29237                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
29238                     editable : false,
29239                     triggerAction: 'all',
29240                     emptyText:'Select',
29241                     selectOnFocus:true,
29242                     width: item.width ? item.width  : 130,
29243                     listeners : {
29244                         'select': function(c, r, i) {
29245                              
29246                             
29247                             if (c.stylename) {
29248                                 tb.selectedNode.style[c.stylename] =  r.get('val');
29249                                 editorcore.syncValue();
29250                                 return;
29251                             }
29252                             if (r === false) {
29253                                 tb.selectedNode.removeAttribute(c.attrname);
29254                                 editorcore.syncValue();
29255                                 return;
29256                             }
29257                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
29258                             editorcore.syncValue();
29259                         }
29260                     }
29261
29262                 }));
29263                 continue;
29264                     
29265                  
29266                 /*
29267                 tb.addField( new Roo.form.TextField({
29268                     name: i,
29269                     width: 100,
29270                     //allowBlank:false,
29271                     value: ''
29272                 }));
29273                 continue;
29274                 */
29275             }
29276             tb.addField( new Roo.form.TextField({
29277                 name: '-roo-edit-' + tlist[i].name,
29278                 attrname : tlist[i].name,
29279                 
29280                 width: item.width,
29281                 //allowBlank:true,
29282                 value: '',
29283                 listeners: {
29284                     'change' : function(f, nv, ov) {
29285                         
29286                          
29287                         tb.selectedNode.setAttribute(f.attrname, nv);
29288                         editorcore.syncValue();
29289                     }
29290                 }
29291             }));
29292              
29293         }
29294         
29295         var _this = this;
29296         var show_delete = !block || block.deleteTitle !== false;
29297         if(nm == 'BODY'){
29298             show_delete = false;
29299             tb.addSeparator();
29300         
29301             tb.addButton( {
29302                 text: 'Stylesheets',
29303
29304                 listeners : {
29305                     click : function ()
29306                     {
29307                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
29308                     }
29309                 }
29310             });
29311         }
29312         
29313         tb.addFill();
29314         if (show_delete) {
29315             tb.addButton({
29316                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
29317         
29318                 listeners : {
29319                     click : function ()
29320                     {
29321                         var sn = tb.selectedNode;
29322                         if (block) {
29323                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
29324                             
29325                         }
29326                         if (!sn) {
29327                             return;
29328                         }
29329                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
29330                         if (sn.hasAttribute('data-block')) {
29331                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
29332                             sn.parentNode.removeChild(sn);
29333                             
29334                         } else if (sn && sn.tagName != 'BODY') {
29335                             // remove and keep parents.
29336                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
29337                             a.replaceTag(sn);
29338                         }
29339                         
29340                         
29341                         var range = editorcore.createRange();
29342             
29343                         range.setStart(stn,0);
29344                         range.setEnd(stn,0); 
29345                         var selection = editorcore.getSelection();
29346                         selection.removeAllRanges();
29347                         selection.addRange(range);
29348                         
29349                         
29350                         //_this.updateToolbar(null, null, pn);
29351                         _this.updateToolbar(null, null, null);
29352                         _this.updateFooter(false);
29353                         
29354                     }
29355                 }
29356                 
29357                         
29358                     
29359                 
29360             });
29361         }    
29362         
29363         tb.el.on('click', function(e){
29364             e.preventDefault(); // what does this do?
29365         });
29366         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
29367         tb.el.hide();
29368         
29369         // dont need to disable them... as they will get hidden
29370         return tb;
29371          
29372         
29373     },
29374     buildFooter : function()
29375     {
29376         
29377         var fel = this.editor.wrap.createChild();
29378         this.footer = new Roo.Toolbar(fel);
29379         // toolbar has scrolly on left / right?
29380         var footDisp= new Roo.Toolbar.Fill();
29381         var _t = this;
29382         this.footer.add(
29383             {
29384                 text : '&lt;',
29385                 xtype: 'Button',
29386                 handler : function() {
29387                     _t.footDisp.scrollTo('left',0,true)
29388                 }
29389             }
29390         );
29391         this.footer.add( footDisp );
29392         this.footer.add( 
29393             {
29394                 text : '&gt;',
29395                 xtype: 'Button',
29396                 handler : function() {
29397                     // no animation..
29398                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
29399                 }
29400             }
29401         );
29402         var fel = Roo.get(footDisp.el);
29403         fel.addClass('x-editor-context');
29404         this.footDispWrap = fel; 
29405         this.footDispWrap.overflow  = 'hidden';
29406         
29407         this.footDisp = fel.createChild();
29408         this.footDispWrap.on('click', this.onContextClick, this)
29409         
29410         
29411     },
29412     // when the footer contect changes
29413     onContextClick : function (ev,dom)
29414     {
29415         ev.preventDefault();
29416         var  cn = dom.className;
29417         //Roo.log(cn);
29418         if (!cn.match(/x-ed-loc-/)) {
29419             return;
29420         }
29421         var n = cn.split('-').pop();
29422         var ans = this.footerEls;
29423         var sel = ans[n];
29424         
29425         this.editorcore.selectNode(sel);
29426         
29427         
29428         this.updateToolbar(null, null, sel);
29429         
29430         
29431     }
29432     
29433     
29434     
29435     
29436     
29437 });
29438
29439
29440
29441
29442
29443 /*
29444  * Based on:
29445  * Ext JS Library 1.1.1
29446  * Copyright(c) 2006-2007, Ext JS, LLC.
29447  *
29448  * Originally Released Under LGPL - original licence link has changed is not relivant.
29449  *
29450  * Fork - LGPL
29451  * <script type="text/javascript">
29452  */
29453  
29454 /**
29455  * @class Roo.form.BasicForm
29456  * @extends Roo.util.Observable
29457  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
29458  * @constructor
29459  * @param {String/HTMLElement/Roo.Element} el The form element or its id
29460  * @param {Object} config Configuration options
29461  */
29462 Roo.form.BasicForm = function(el, config){
29463     this.allItems = [];
29464     this.childForms = [];
29465     Roo.apply(this, config);
29466     /*
29467      * The Roo.form.Field items in this form.
29468      * @type MixedCollection
29469      */
29470      
29471      
29472     this.items = new Roo.util.MixedCollection(false, function(o){
29473         return o.id || (o.id = Roo.id());
29474     });
29475     this.addEvents({
29476         /**
29477          * @event beforeaction
29478          * Fires before any action is performed. Return false to cancel the action.
29479          * @param {Form} this
29480          * @param {Action} action The action to be performed
29481          */
29482         beforeaction: true,
29483         /**
29484          * @event actionfailed
29485          * Fires when an action fails.
29486          * @param {Form} this
29487          * @param {Action} action The action that failed
29488          */
29489         actionfailed : true,
29490         /**
29491          * @event actioncomplete
29492          * Fires when an action is completed.
29493          * @param {Form} this
29494          * @param {Action} action The action that completed
29495          */
29496         actioncomplete : true
29497     });
29498     if(el){
29499         this.initEl(el);
29500     }
29501     Roo.form.BasicForm.superclass.constructor.call(this);
29502     
29503     Roo.form.BasicForm.popover.apply();
29504 };
29505
29506 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
29507     /**
29508      * @cfg {String} method
29509      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
29510      */
29511     /**
29512      * @cfg {DataReader} reader
29513      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
29514      * This is optional as there is built-in support for processing JSON.
29515      */
29516     /**
29517      * @cfg {DataReader} errorReader
29518      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
29519      * This is completely optional as there is built-in support for processing JSON.
29520      */
29521     /**
29522      * @cfg {String} url
29523      * The URL to use for form actions if one isn't supplied in the action options.
29524      */
29525     /**
29526      * @cfg {Boolean} fileUpload
29527      * Set to true if this form is a file upload.
29528      */
29529      
29530     /**
29531      * @cfg {Object} baseParams
29532      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
29533      */
29534      /**
29535      
29536     /**
29537      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
29538      */
29539     timeout: 30,
29540
29541     // private
29542     activeAction : null,
29543
29544     /**
29545      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
29546      * or setValues() data instead of when the form was first created.
29547      */
29548     trackResetOnLoad : false,
29549     
29550     
29551     /**
29552      * childForms - used for multi-tab forms
29553      * @type {Array}
29554      */
29555     childForms : false,
29556     
29557     /**
29558      * allItems - full list of fields.
29559      * @type {Array}
29560      */
29561     allItems : false,
29562     
29563     /**
29564      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
29565      * element by passing it or its id or mask the form itself by passing in true.
29566      * @type Mixed
29567      */
29568     waitMsgTarget : false,
29569     
29570     /**
29571      * @type Boolean
29572      */
29573     disableMask : false,
29574     
29575     /**
29576      * @cfg {Boolean} errorMask (true|false) default false
29577      */
29578     errorMask : false,
29579     
29580     /**
29581      * @cfg {Number} maskOffset Default 100
29582      */
29583     maskOffset : 100,
29584
29585     // private
29586     initEl : function(el){
29587         this.el = Roo.get(el);
29588         this.id = this.el.id || Roo.id();
29589         this.el.on('submit', this.onSubmit, this);
29590         this.el.addClass('x-form');
29591     },
29592
29593     // private
29594     onSubmit : function(e){
29595         e.stopEvent();
29596     },
29597
29598     /**
29599      * Returns true if client-side validation on the form is successful.
29600      * @return Boolean
29601      */
29602     isValid : function(){
29603         var valid = true;
29604         var target = false;
29605         this.items.each(function(f){
29606             if(f.validate()){
29607                 return;
29608             }
29609             
29610             valid = false;
29611                 
29612             if(!target && f.el.isVisible(true)){
29613                 target = f;
29614             }
29615         });
29616         
29617         if(this.errorMask && !valid){
29618             Roo.form.BasicForm.popover.mask(this, target);
29619         }
29620         
29621         return valid;
29622     },
29623     /**
29624      * Returns array of invalid form fields.
29625      * @return Array
29626      */
29627     
29628     invalidFields : function()
29629     {
29630         var ret = [];
29631         this.items.each(function(f){
29632             if(f.validate()){
29633                 return;
29634             }
29635             ret.push(f);
29636             
29637         });
29638         
29639         return ret;
29640     },
29641     
29642     
29643     /**
29644      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
29645      * @return Boolean
29646      */
29647     isDirty : function(){
29648         var dirty = false;
29649         this.items.each(function(f){
29650            if(f.isDirty()){
29651                dirty = true;
29652                return false;
29653            }
29654         });
29655         return dirty;
29656     },
29657     
29658     /**
29659      * Returns true if any fields in this form have changed since their original load. (New version)
29660      * @return Boolean
29661      */
29662     
29663     hasChanged : function()
29664     {
29665         var dirty = false;
29666         this.items.each(function(f){
29667            if(f.hasChanged()){
29668                dirty = true;
29669                return false;
29670            }
29671         });
29672         return dirty;
29673         
29674     },
29675     /**
29676      * Resets all hasChanged to 'false' -
29677      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29678      * So hasChanged storage is only to be used for this purpose
29679      * @return Boolean
29680      */
29681     resetHasChanged : function()
29682     {
29683         this.items.each(function(f){
29684            f.resetHasChanged();
29685         });
29686         
29687     },
29688     
29689     
29690     /**
29691      * Performs a predefined action (submit or load) or custom actions you define on this form.
29692      * @param {String} actionName The name of the action type
29693      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
29694      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29695      * accept other config options):
29696      * <pre>
29697 Property          Type             Description
29698 ----------------  ---------------  ----------------------------------------------------------------------------------
29699 url               String           The url for the action (defaults to the form's url)
29700 method            String           The form method to use (defaults to the form's method, or POST if not defined)
29701 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
29702 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
29703                                    validate the form on the client (defaults to false)
29704      * </pre>
29705      * @return {BasicForm} this
29706      */
29707     doAction : function(action, options){
29708         if(typeof action == 'string'){
29709             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29710         }
29711         if(this.fireEvent('beforeaction', this, action) !== false){
29712             this.beforeAction(action);
29713             action.run.defer(100, action);
29714         }
29715         return this;
29716     },
29717
29718     /**
29719      * Shortcut to do a submit action.
29720      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29721      * @return {BasicForm} this
29722      */
29723     submit : function(options){
29724         this.doAction('submit', options);
29725         return this;
29726     },
29727
29728     /**
29729      * Shortcut to do a load action.
29730      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29731      * @return {BasicForm} this
29732      */
29733     load : function(options){
29734         this.doAction('load', options);
29735         return this;
29736     },
29737
29738     /**
29739      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29740      * @param {Record} record The record to edit
29741      * @return {BasicForm} this
29742      */
29743     updateRecord : function(record){
29744         record.beginEdit();
29745         var fs = record.fields;
29746         fs.each(function(f){
29747             var field = this.findField(f.name);
29748             if(field){
29749                 record.set(f.name, field.getValue());
29750             }
29751         }, this);
29752         record.endEdit();
29753         return this;
29754     },
29755
29756     /**
29757      * Loads an Roo.data.Record into this form.
29758      * @param {Record} record The record to load
29759      * @return {BasicForm} this
29760      */
29761     loadRecord : function(record){
29762         this.setValues(record.data);
29763         return this;
29764     },
29765
29766     // private
29767     beforeAction : function(action){
29768         var o = action.options;
29769         
29770         if(!this.disableMask) {
29771             if(this.waitMsgTarget === true){
29772                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29773             }else if(this.waitMsgTarget){
29774                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29775                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29776             }else {
29777                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29778             }
29779         }
29780         
29781          
29782     },
29783
29784     // private
29785     afterAction : function(action, success){
29786         this.activeAction = null;
29787         var o = action.options;
29788         
29789         if(!this.disableMask) {
29790             if(this.waitMsgTarget === true){
29791                 this.el.unmask();
29792             }else if(this.waitMsgTarget){
29793                 this.waitMsgTarget.unmask();
29794             }else{
29795                 Roo.MessageBox.updateProgress(1);
29796                 Roo.MessageBox.hide();
29797             }
29798         }
29799         
29800         if(success){
29801             if(o.reset){
29802                 this.reset();
29803             }
29804             Roo.callback(o.success, o.scope, [this, action]);
29805             this.fireEvent('actioncomplete', this, action);
29806             
29807         }else{
29808             
29809             // failure condition..
29810             // we have a scenario where updates need confirming.
29811             // eg. if a locking scenario exists..
29812             // we look for { errors : { needs_confirm : true }} in the response.
29813             if (
29814                 (typeof(action.result) != 'undefined')  &&
29815                 (typeof(action.result.errors) != 'undefined')  &&
29816                 (typeof(action.result.errors.needs_confirm) != 'undefined')
29817            ){
29818                 var _t = this;
29819                 Roo.MessageBox.confirm(
29820                     "Change requires confirmation",
29821                     action.result.errorMsg,
29822                     function(r) {
29823                         if (r != 'yes') {
29824                             return;
29825                         }
29826                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
29827                     }
29828                     
29829                 );
29830                 
29831                 
29832                 
29833                 return;
29834             }
29835             
29836             Roo.callback(o.failure, o.scope, [this, action]);
29837             // show an error message if no failed handler is set..
29838             if (!this.hasListener('actionfailed')) {
29839                 Roo.MessageBox.alert("Error",
29840                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29841                         action.result.errorMsg :
29842                         "Saving Failed, please check your entries or try again"
29843                 );
29844             }
29845             
29846             this.fireEvent('actionfailed', this, action);
29847         }
29848         
29849     },
29850
29851     /**
29852      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29853      * @param {String} id The value to search for
29854      * @return Field
29855      */
29856     findField : function(id){
29857         var field = this.items.get(id);
29858         if(!field){
29859             this.items.each(function(f){
29860                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29861                     field = f;
29862                     return false;
29863                 }
29864             });
29865         }
29866         return field || null;
29867     },
29868
29869     /**
29870      * Add a secondary form to this one, 
29871      * Used to provide tabbed forms. One form is primary, with hidden values 
29872      * which mirror the elements from the other forms.
29873      * 
29874      * @param {Roo.form.Form} form to add.
29875      * 
29876      */
29877     addForm : function(form)
29878     {
29879        
29880         if (this.childForms.indexOf(form) > -1) {
29881             // already added..
29882             return;
29883         }
29884         this.childForms.push(form);
29885         var n = '';
29886         Roo.each(form.allItems, function (fe) {
29887             
29888             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29889             if (this.findField(n)) { // already added..
29890                 return;
29891             }
29892             var add = new Roo.form.Hidden({
29893                 name : n
29894             });
29895             add.render(this.el);
29896             
29897             this.add( add );
29898         }, this);
29899         
29900     },
29901     /**
29902      * Mark fields in this form invalid in bulk.
29903      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29904      * @return {BasicForm} this
29905      */
29906     markInvalid : function(errors){
29907         if(errors instanceof Array){
29908             for(var i = 0, len = errors.length; i < len; i++){
29909                 var fieldError = errors[i];
29910                 var f = this.findField(fieldError.id);
29911                 if(f){
29912                     f.markInvalid(fieldError.msg);
29913                 }
29914             }
29915         }else{
29916             var field, id;
29917             for(id in errors){
29918                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29919                     field.markInvalid(errors[id]);
29920                 }
29921             }
29922         }
29923         Roo.each(this.childForms || [], function (f) {
29924             f.markInvalid(errors);
29925         });
29926         
29927         return this;
29928     },
29929
29930     /**
29931      * Set values for fields in this form in bulk.
29932      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29933      * @return {BasicForm} this
29934      */
29935     setValues : function(values){
29936         if(values instanceof Array){ // array of objects
29937             for(var i = 0, len = values.length; i < len; i++){
29938                 var v = values[i];
29939                 var f = this.findField(v.id);
29940                 if(f){
29941                     f.setValue(v.value);
29942                     if(this.trackResetOnLoad){
29943                         f.originalValue = f.getValue();
29944                     }
29945                 }
29946             }
29947         }else{ // object hash
29948             var field, id;
29949             for(id in values){
29950                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29951                     
29952                     
29953                     
29954                     
29955                     if (field.setFromData && 
29956                         field.valueField && 
29957                         field.displayField &&
29958                         // combos' with local stores can 
29959                         // be queried via setValue()
29960                         // to set their value..
29961                         (field.store && !field.store.isLocal)
29962                         ) {
29963                         // it's a combo
29964                         var sd = { };
29965                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29966                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29967                         field.setFromData(sd);
29968                         
29969                     } else if (field.inputType && field.inputType == 'radio') {
29970                         
29971                         field.setValue(values[id]);
29972                     } else {
29973                         field.setValue(values[id]);
29974                     }
29975                     
29976                     
29977                     if(this.trackResetOnLoad){
29978                         field.originalValue = field.getValue();
29979                     }
29980                 }
29981             }
29982         }
29983         this.resetHasChanged();
29984         
29985         
29986         Roo.each(this.childForms || [], function (f) {
29987             f.setValues(values);
29988             f.resetHasChanged();
29989         });
29990                 
29991         return this;
29992     },
29993  
29994     /**
29995      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29996      * they are returned as an array.
29997      * @param {Boolean} asString (def)
29998      * @return {Object}
29999      */
30000     getValues : function(asString)
30001     {
30002         if (this.childForms) {
30003             // copy values from the child forms
30004             Roo.each(this.childForms, function (f) {
30005                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
30006             }, this);
30007         }
30008         
30009         // use formdata
30010         if (typeof(FormData) != 'undefined' && asString !== true) {
30011             // this relies on a 'recent' version of chrome apparently...
30012             try {
30013                 var fd = (new FormData(this.el.dom)).entries();
30014                 var ret = {};
30015                 var ent = fd.next();
30016                 while (!ent.done) {
30017                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
30018                     ent = fd.next();
30019                 };
30020                 return ret;
30021             } catch(e) {
30022                 
30023             }
30024             
30025         }
30026         
30027         
30028         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
30029         if(asString === true){
30030             return fs;
30031         }
30032         return Roo.urlDecode(fs);
30033     },
30034     
30035     /**
30036      * Returns the fields in this form as an object with key/value pairs. 
30037      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
30038      * Normally this will not return readOnly data 
30039      * @param {Boolean} with_readonly return readonly field data.
30040      * @return {Object}
30041      */
30042     getFieldValues : function(with_readonly)
30043     {
30044         if (this.childForms) {
30045             // copy values from the child forms
30046             // should this call getFieldValues - probably not as we do not currently copy
30047             // hidden fields when we generate..
30048             Roo.each(this.childForms, function (f) {
30049                 this.setValues(f.getFieldValues());
30050             }, this);
30051         }
30052         
30053         var ret = {};
30054         this.items.each(function(f){
30055             
30056             if (f.readOnly && with_readonly !== true) {
30057                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
30058                         // if a subform contains a copy of them.
30059                         // if you have subforms with the same editable data, you will need to copy the data back
30060                         // and forth.
30061             }
30062             
30063             if (!f.getName()) {
30064                 return;
30065             }
30066             var v = f.getValue();
30067             if (f.inputType =='radio') {
30068                 if (typeof(ret[f.getName()]) == 'undefined') {
30069                     ret[f.getName()] = ''; // empty..
30070                 }
30071                 
30072                 if (!f.el.dom.checked) {
30073                     return;
30074                     
30075                 }
30076                 v = f.el.dom.value;
30077                 
30078             }
30079             
30080             // not sure if this supported any more..
30081             if ((typeof(v) == 'object') && f.getRawValue) {
30082                 v = f.getRawValue() ; // dates..
30083             }
30084             // combo boxes where name != hiddenName...
30085             if (f.name != f.getName()) {
30086                 ret[f.name] = f.getRawValue();
30087             }
30088             ret[f.getName()] = v;
30089         });
30090         
30091         return ret;
30092     },
30093
30094     /**
30095      * Clears all invalid messages in this form.
30096      * @return {BasicForm} this
30097      */
30098     clearInvalid : function(){
30099         this.items.each(function(f){
30100            f.clearInvalid();
30101         });
30102         
30103         Roo.each(this.childForms || [], function (f) {
30104             f.clearInvalid();
30105         });
30106         
30107         
30108         return this;
30109     },
30110
30111     /**
30112      * Resets this form.
30113      * @return {BasicForm} this
30114      */
30115     reset : function(){
30116         this.items.each(function(f){
30117             f.reset();
30118         });
30119         
30120         Roo.each(this.childForms || [], function (f) {
30121             f.reset();
30122         });
30123         this.resetHasChanged();
30124         
30125         return this;
30126     },
30127
30128     /**
30129      * Add Roo.form components to this form.
30130      * @param {Field} field1
30131      * @param {Field} field2 (optional)
30132      * @param {Field} etc (optional)
30133      * @return {BasicForm} this
30134      */
30135     add : function(){
30136         this.items.addAll(Array.prototype.slice.call(arguments, 0));
30137         return this;
30138     },
30139
30140
30141     /**
30142      * Removes a field from the items collection (does NOT remove its markup).
30143      * @param {Field} field
30144      * @return {BasicForm} this
30145      */
30146     remove : function(field){
30147         this.items.remove(field);
30148         return this;
30149     },
30150
30151     /**
30152      * Looks at the fields in this form, checks them for an id attribute,
30153      * and calls applyTo on the existing dom element with that id.
30154      * @return {BasicForm} this
30155      */
30156     render : function(){
30157         this.items.each(function(f){
30158             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
30159                 f.applyTo(f.id);
30160             }
30161         });
30162         return this;
30163     },
30164
30165     /**
30166      * Calls {@link Ext#apply} for all fields in this form with the passed object.
30167      * @param {Object} values
30168      * @return {BasicForm} this
30169      */
30170     applyToFields : function(o){
30171         this.items.each(function(f){
30172            Roo.apply(f, o);
30173         });
30174         return this;
30175     },
30176
30177     /**
30178      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
30179      * @param {Object} values
30180      * @return {BasicForm} this
30181      */
30182     applyIfToFields : function(o){
30183         this.items.each(function(f){
30184            Roo.applyIf(f, o);
30185         });
30186         return this;
30187     }
30188 });
30189
30190 // back compat
30191 Roo.BasicForm = Roo.form.BasicForm;
30192
30193 Roo.apply(Roo.form.BasicForm, {
30194     
30195     popover : {
30196         
30197         padding : 5,
30198         
30199         isApplied : false,
30200         
30201         isMasked : false,
30202         
30203         form : false,
30204         
30205         target : false,
30206         
30207         intervalID : false,
30208         
30209         maskEl : false,
30210         
30211         apply : function()
30212         {
30213             if(this.isApplied){
30214                 return;
30215             }
30216             
30217             this.maskEl = {
30218                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
30219                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
30220                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
30221                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
30222             };
30223             
30224             this.maskEl.top.enableDisplayMode("block");
30225             this.maskEl.left.enableDisplayMode("block");
30226             this.maskEl.bottom.enableDisplayMode("block");
30227             this.maskEl.right.enableDisplayMode("block");
30228             
30229             Roo.get(document.body).on('click', function(){
30230                 this.unmask();
30231             }, this);
30232             
30233             Roo.get(document.body).on('touchstart', function(){
30234                 this.unmask();
30235             }, this);
30236             
30237             this.isApplied = true
30238         },
30239         
30240         mask : function(form, target)
30241         {
30242             this.form = form;
30243             
30244             this.target = target;
30245             
30246             if(!this.form.errorMask || !target.el){
30247                 return;
30248             }
30249             
30250             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
30251             
30252             var ot = this.target.el.calcOffsetsTo(scrollable);
30253             
30254             var scrollTo = ot[1] - this.form.maskOffset;
30255             
30256             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
30257             
30258             scrollable.scrollTo('top', scrollTo);
30259             
30260             var el = this.target.wrap || this.target.el;
30261             
30262             var box = el.getBox();
30263             
30264             this.maskEl.top.setStyle('position', 'absolute');
30265             this.maskEl.top.setStyle('z-index', 10000);
30266             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
30267             this.maskEl.top.setLeft(0);
30268             this.maskEl.top.setTop(0);
30269             this.maskEl.top.show();
30270             
30271             this.maskEl.left.setStyle('position', 'absolute');
30272             this.maskEl.left.setStyle('z-index', 10000);
30273             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
30274             this.maskEl.left.setLeft(0);
30275             this.maskEl.left.setTop(box.y - this.padding);
30276             this.maskEl.left.show();
30277
30278             this.maskEl.bottom.setStyle('position', 'absolute');
30279             this.maskEl.bottom.setStyle('z-index', 10000);
30280             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
30281             this.maskEl.bottom.setLeft(0);
30282             this.maskEl.bottom.setTop(box.bottom + this.padding);
30283             this.maskEl.bottom.show();
30284
30285             this.maskEl.right.setStyle('position', 'absolute');
30286             this.maskEl.right.setStyle('z-index', 10000);
30287             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
30288             this.maskEl.right.setLeft(box.right + this.padding);
30289             this.maskEl.right.setTop(box.y - this.padding);
30290             this.maskEl.right.show();
30291
30292             this.intervalID = window.setInterval(function() {
30293                 Roo.form.BasicForm.popover.unmask();
30294             }, 10000);
30295
30296             window.onwheel = function(){ return false;};
30297             
30298             (function(){ this.isMasked = true; }).defer(500, this);
30299             
30300         },
30301         
30302         unmask : function()
30303         {
30304             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
30305                 return;
30306             }
30307             
30308             this.maskEl.top.setStyle('position', 'absolute');
30309             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
30310             this.maskEl.top.hide();
30311
30312             this.maskEl.left.setStyle('position', 'absolute');
30313             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
30314             this.maskEl.left.hide();
30315
30316             this.maskEl.bottom.setStyle('position', 'absolute');
30317             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
30318             this.maskEl.bottom.hide();
30319
30320             this.maskEl.right.setStyle('position', 'absolute');
30321             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
30322             this.maskEl.right.hide();
30323             
30324             window.onwheel = function(){ return true;};
30325             
30326             if(this.intervalID){
30327                 window.clearInterval(this.intervalID);
30328                 this.intervalID = false;
30329             }
30330             
30331             this.isMasked = false;
30332             
30333         }
30334         
30335     }
30336     
30337 });/*
30338  * Based on:
30339  * Ext JS Library 1.1.1
30340  * Copyright(c) 2006-2007, Ext JS, LLC.
30341  *
30342  * Originally Released Under LGPL - original licence link has changed is not relivant.
30343  *
30344  * Fork - LGPL
30345  * <script type="text/javascript">
30346  */
30347
30348 /**
30349  * @class Roo.form.Form
30350  * @extends Roo.form.BasicForm
30351  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
30352  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
30353  * @constructor
30354  * @param {Object} config Configuration options
30355  */
30356 Roo.form.Form = function(config){
30357     var xitems =  [];
30358     if (config.items) {
30359         xitems = config.items;
30360         delete config.items;
30361     }
30362    
30363     
30364     Roo.form.Form.superclass.constructor.call(this, null, config);
30365     this.url = this.url || this.action;
30366     if(!this.root){
30367         this.root = new Roo.form.Layout(Roo.applyIf({
30368             id: Roo.id()
30369         }, config));
30370     }
30371     this.active = this.root;
30372     /**
30373      * Array of all the buttons that have been added to this form via {@link addButton}
30374      * @type Array
30375      */
30376     this.buttons = [];
30377     this.allItems = [];
30378     this.addEvents({
30379         /**
30380          * @event clientvalidation
30381          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
30382          * @param {Form} this
30383          * @param {Boolean} valid true if the form has passed client-side validation
30384          */
30385         clientvalidation: true,
30386         /**
30387          * @event rendered
30388          * Fires when the form is rendered
30389          * @param {Roo.form.Form} form
30390          */
30391         rendered : true
30392     });
30393     
30394     if (this.progressUrl) {
30395             // push a hidden field onto the list of fields..
30396             this.addxtype( {
30397                     xns: Roo.form, 
30398                     xtype : 'Hidden', 
30399                     name : 'UPLOAD_IDENTIFIER' 
30400             });
30401         }
30402         
30403     
30404     Roo.each(xitems, this.addxtype, this);
30405     
30406 };
30407
30408 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
30409      /**
30410      * @cfg {Roo.Button} buttons[] buttons at bottom of form
30411      */
30412     
30413     /**
30414      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
30415      */
30416     /**
30417      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
30418      */
30419     /**
30420      * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
30421      */
30422     buttonAlign:'center',
30423
30424     /**
30425      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
30426      */
30427     minButtonWidth:75,
30428
30429     /**
30430      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
30431      * This property cascades to child containers if not set.
30432      */
30433     labelAlign:'left',
30434
30435     /**
30436      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
30437      * fires a looping event with that state. This is required to bind buttons to the valid
30438      * state using the config value formBind:true on the button.
30439      */
30440     monitorValid : false,
30441
30442     /**
30443      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
30444      */
30445     monitorPoll : 200,
30446     
30447     /**
30448      * @cfg {String} progressUrl - Url to return progress data 
30449      */
30450     
30451     progressUrl : false,
30452     /**
30453      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
30454      * sending a formdata with extra parameters - eg uploaded elements.
30455      */
30456     
30457     formData : false,
30458     
30459     /**
30460      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
30461      * fields are added and the column is closed. If no fields are passed the column remains open
30462      * until end() is called.
30463      * @param {Object} config The config to pass to the column
30464      * @param {Field} field1 (optional)
30465      * @param {Field} field2 (optional)
30466      * @param {Field} etc (optional)
30467      * @return Column The column container object
30468      */
30469     column : function(c){
30470         var col = new Roo.form.Column(c);
30471         this.start(col);
30472         if(arguments.length > 1){ // duplicate code required because of Opera
30473             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30474             this.end();
30475         }
30476         return col;
30477     },
30478
30479     /**
30480      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
30481      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
30482      * until end() is called.
30483      * @param {Object} config The config to pass to the fieldset
30484      * @param {Field} field1 (optional)
30485      * @param {Field} field2 (optional)
30486      * @param {Field} etc (optional)
30487      * @return FieldSet The fieldset container object
30488      */
30489     fieldset : function(c){
30490         var fs = new Roo.form.FieldSet(c);
30491         this.start(fs);
30492         if(arguments.length > 1){ // duplicate code required because of Opera
30493             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30494             this.end();
30495         }
30496         return fs;
30497     },
30498
30499     /**
30500      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
30501      * fields are added and the container is closed. If no fields are passed the container remains open
30502      * until end() is called.
30503      * @param {Object} config The config to pass to the Layout
30504      * @param {Field} field1 (optional)
30505      * @param {Field} field2 (optional)
30506      * @param {Field} etc (optional)
30507      * @return Layout The container object
30508      */
30509     container : function(c){
30510         var l = new Roo.form.Layout(c);
30511         this.start(l);
30512         if(arguments.length > 1){ // duplicate code required because of Opera
30513             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30514             this.end();
30515         }
30516         return l;
30517     },
30518
30519     /**
30520      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
30521      * @param {Object} container A Roo.form.Layout or subclass of Layout
30522      * @return {Form} this
30523      */
30524     start : function(c){
30525         // cascade label info
30526         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
30527         this.active.stack.push(c);
30528         c.ownerCt = this.active;
30529         this.active = c;
30530         return this;
30531     },
30532
30533     /**
30534      * Closes the current open container
30535      * @return {Form} this
30536      */
30537     end : function(){
30538         if(this.active == this.root){
30539             return this;
30540         }
30541         this.active = this.active.ownerCt;
30542         return this;
30543     },
30544
30545     /**
30546      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
30547      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
30548      * as the label of the field.
30549      * @param {Field} field1
30550      * @param {Field} field2 (optional)
30551      * @param {Field} etc. (optional)
30552      * @return {Form} this
30553      */
30554     add : function(){
30555         this.active.stack.push.apply(this.active.stack, arguments);
30556         this.allItems.push.apply(this.allItems,arguments);
30557         var r = [];
30558         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
30559             if(a[i].isFormField){
30560                 r.push(a[i]);
30561             }
30562         }
30563         if(r.length > 0){
30564             Roo.form.Form.superclass.add.apply(this, r);
30565         }
30566         return this;
30567     },
30568     
30569
30570     
30571     
30572     
30573      /**
30574      * Find any element that has been added to a form, using it's ID or name
30575      * This can include framesets, columns etc. along with regular fields..
30576      * @param {String} id - id or name to find.
30577      
30578      * @return {Element} e - or false if nothing found.
30579      */
30580     findbyId : function(id)
30581     {
30582         var ret = false;
30583         if (!id) {
30584             return ret;
30585         }
30586         Roo.each(this.allItems, function(f){
30587             if (f.id == id || f.name == id ){
30588                 ret = f;
30589                 return false;
30590             }
30591         });
30592         return ret;
30593     },
30594
30595     
30596     
30597     /**
30598      * Render this form into the passed container. This should only be called once!
30599      * @param {String/HTMLElement/Element} container The element this component should be rendered into
30600      * @return {Form} this
30601      */
30602     render : function(ct)
30603     {
30604         
30605         
30606         
30607         ct = Roo.get(ct);
30608         var o = this.autoCreate || {
30609             tag: 'form',
30610             method : this.method || 'POST',
30611             id : this.id || Roo.id()
30612         };
30613         this.initEl(ct.createChild(o));
30614
30615         this.root.render(this.el);
30616         
30617        
30618              
30619         this.items.each(function(f){
30620             f.render('x-form-el-'+f.id);
30621         });
30622
30623         if(this.buttons.length > 0){
30624             // tables are required to maintain order and for correct IE layout
30625             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30626                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30627                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30628             }}, null, true);
30629             var tr = tb.getElementsByTagName('tr')[0];
30630             for(var i = 0, len = this.buttons.length; i < len; i++) {
30631                 var b = this.buttons[i];
30632                 var td = document.createElement('td');
30633                 td.className = 'x-form-btn-td';
30634                 b.render(tr.appendChild(td));
30635             }
30636         }
30637         if(this.monitorValid){ // initialize after render
30638             this.startMonitoring();
30639         }
30640         this.fireEvent('rendered', this);
30641         return this;
30642     },
30643
30644     /**
30645      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30646      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30647      * object or a valid Roo.DomHelper element config
30648      * @param {Function} handler The function called when the button is clicked
30649      * @param {Object} scope (optional) The scope of the handler function
30650      * @return {Roo.Button}
30651      */
30652     addButton : function(config, handler, scope){
30653         var bc = {
30654             handler: handler,
30655             scope: scope,
30656             minWidth: this.minButtonWidth,
30657             hideParent:true
30658         };
30659         if(typeof config == "string"){
30660             bc.text = config;
30661         }else{
30662             Roo.apply(bc, config);
30663         }
30664         var btn = new Roo.Button(null, bc);
30665         this.buttons.push(btn);
30666         return btn;
30667     },
30668
30669      /**
30670      * Adds a series of form elements (using the xtype property as the factory method.
30671      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30672      * @param {Object} config 
30673      */
30674     
30675     addxtype : function()
30676     {
30677         var ar = Array.prototype.slice.call(arguments, 0);
30678         var ret = false;
30679         for(var i = 0; i < ar.length; i++) {
30680             if (!ar[i]) {
30681                 continue; // skip -- if this happends something invalid got sent, we 
30682                 // should ignore it, as basically that interface element will not show up
30683                 // and that should be pretty obvious!!
30684             }
30685             
30686             if (Roo.form[ar[i].xtype]) {
30687                 ar[i].form = this;
30688                 var fe = Roo.factory(ar[i], Roo.form);
30689                 if (!ret) {
30690                     ret = fe;
30691                 }
30692                 fe.form = this;
30693                 if (fe.store) {
30694                     fe.store.form = this;
30695                 }
30696                 if (fe.isLayout) {  
30697                          
30698                     this.start(fe);
30699                     this.allItems.push(fe);
30700                     if (fe.items && fe.addxtype) {
30701                         fe.addxtype.apply(fe, fe.items);
30702                         delete fe.items;
30703                     }
30704                      this.end();
30705                     continue;
30706                 }
30707                 
30708                 
30709                  
30710                 this.add(fe);
30711               //  console.log('adding ' + ar[i].xtype);
30712             }
30713             if (ar[i].xtype == 'Button') {  
30714                 //console.log('adding button');
30715                 //console.log(ar[i]);
30716                 this.addButton(ar[i]);
30717                 this.allItems.push(fe);
30718                 continue;
30719             }
30720             
30721             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30722                 alert('end is not supported on xtype any more, use items');
30723             //    this.end();
30724             //    //console.log('adding end');
30725             }
30726             
30727         }
30728         return ret;
30729     },
30730     
30731     /**
30732      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30733      * option "monitorValid"
30734      */
30735     startMonitoring : function(){
30736         if(!this.bound){
30737             this.bound = true;
30738             Roo.TaskMgr.start({
30739                 run : this.bindHandler,
30740                 interval : this.monitorPoll || 200,
30741                 scope: this
30742             });
30743         }
30744     },
30745
30746     /**
30747      * Stops monitoring of the valid state of this form
30748      */
30749     stopMonitoring : function(){
30750         this.bound = false;
30751     },
30752
30753     // private
30754     bindHandler : function(){
30755         if(!this.bound){
30756             return false; // stops binding
30757         }
30758         var valid = true;
30759         this.items.each(function(f){
30760             if(!f.isValid(true)){
30761                 valid = false;
30762                 return false;
30763             }
30764         });
30765         for(var i = 0, len = this.buttons.length; i < len; i++){
30766             var btn = this.buttons[i];
30767             if(btn.formBind === true && btn.disabled === valid){
30768                 btn.setDisabled(!valid);
30769             }
30770         }
30771         this.fireEvent('clientvalidation', this, valid);
30772     }
30773     
30774     
30775     
30776     
30777     
30778     
30779     
30780     
30781 });
30782
30783
30784 // back compat
30785 Roo.Form = Roo.form.Form;
30786 /*
30787  * Based on:
30788  * Ext JS Library 1.1.1
30789  * Copyright(c) 2006-2007, Ext JS, LLC.
30790  *
30791  * Originally Released Under LGPL - original licence link has changed is not relivant.
30792  *
30793  * Fork - LGPL
30794  * <script type="text/javascript">
30795  */
30796
30797 // as we use this in bootstrap.
30798 Roo.namespace('Roo.form');
30799  /**
30800  * @class Roo.form.Action
30801  * Internal Class used to handle form actions
30802  * @constructor
30803  * @param {Roo.form.BasicForm} el The form element or its id
30804  * @param {Object} config Configuration options
30805  */
30806
30807  
30808  
30809 // define the action interface
30810 Roo.form.Action = function(form, options){
30811     this.form = form;
30812     this.options = options || {};
30813 };
30814 /**
30815  * Client Validation Failed
30816  * @const 
30817  */
30818 Roo.form.Action.CLIENT_INVALID = 'client';
30819 /**
30820  * Server Validation Failed
30821  * @const 
30822  */
30823 Roo.form.Action.SERVER_INVALID = 'server';
30824  /**
30825  * Connect to Server Failed
30826  * @const 
30827  */
30828 Roo.form.Action.CONNECT_FAILURE = 'connect';
30829 /**
30830  * Reading Data from Server Failed
30831  * @const 
30832  */
30833 Roo.form.Action.LOAD_FAILURE = 'load';
30834
30835 Roo.form.Action.prototype = {
30836     type : 'default',
30837     failureType : undefined,
30838     response : undefined,
30839     result : undefined,
30840
30841     // interface method
30842     run : function(options){
30843
30844     },
30845
30846     // interface method
30847     success : function(response){
30848
30849     },
30850
30851     // interface method
30852     handleResponse : function(response){
30853
30854     },
30855
30856     // default connection failure
30857     failure : function(response){
30858         
30859         this.response = response;
30860         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30861         this.form.afterAction(this, false);
30862     },
30863
30864     processResponse : function(response){
30865         this.response = response;
30866         if(!response.responseText){
30867             return true;
30868         }
30869         this.result = this.handleResponse(response);
30870         return this.result;
30871     },
30872
30873     // utility functions used internally
30874     getUrl : function(appendParams){
30875         var url = this.options.url || this.form.url || this.form.el.dom.action;
30876         if(appendParams){
30877             var p = this.getParams();
30878             if(p){
30879                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30880             }
30881         }
30882         return url;
30883     },
30884
30885     getMethod : function(){
30886         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30887     },
30888
30889     getParams : function(){
30890         var bp = this.form.baseParams;
30891         var p = this.options.params;
30892         if(p){
30893             if(typeof p == "object"){
30894                 p = Roo.urlEncode(Roo.applyIf(p, bp));
30895             }else if(typeof p == 'string' && bp){
30896                 p += '&' + Roo.urlEncode(bp);
30897             }
30898         }else if(bp){
30899             p = Roo.urlEncode(bp);
30900         }
30901         return p;
30902     },
30903
30904     createCallback : function(){
30905         return {
30906             success: this.success,
30907             failure: this.failure,
30908             scope: this,
30909             timeout: (this.form.timeout*1000),
30910             upload: this.form.fileUpload ? this.success : undefined
30911         };
30912     }
30913 };
30914
30915 Roo.form.Action.Submit = function(form, options){
30916     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30917 };
30918
30919 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30920     type : 'submit',
30921
30922     haveProgress : false,
30923     uploadComplete : false,
30924     
30925     // uploadProgress indicator.
30926     uploadProgress : function()
30927     {
30928         if (!this.form.progressUrl) {
30929             return;
30930         }
30931         
30932         if (!this.haveProgress) {
30933             Roo.MessageBox.progress("Uploading", "Uploading");
30934         }
30935         if (this.uploadComplete) {
30936            Roo.MessageBox.hide();
30937            return;
30938         }
30939         
30940         this.haveProgress = true;
30941    
30942         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30943         
30944         var c = new Roo.data.Connection();
30945         c.request({
30946             url : this.form.progressUrl,
30947             params: {
30948                 id : uid
30949             },
30950             method: 'GET',
30951             success : function(req){
30952                //console.log(data);
30953                 var rdata = false;
30954                 var edata;
30955                 try  {
30956                    rdata = Roo.decode(req.responseText)
30957                 } catch (e) {
30958                     Roo.log("Invalid data from server..");
30959                     Roo.log(edata);
30960                     return;
30961                 }
30962                 if (!rdata || !rdata.success) {
30963                     Roo.log(rdata);
30964                     Roo.MessageBox.alert(Roo.encode(rdata));
30965                     return;
30966                 }
30967                 var data = rdata.data;
30968                 
30969                 if (this.uploadComplete) {
30970                    Roo.MessageBox.hide();
30971                    return;
30972                 }
30973                    
30974                 if (data){
30975                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30976                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30977                     );
30978                 }
30979                 this.uploadProgress.defer(2000,this);
30980             },
30981        
30982             failure: function(data) {
30983                 Roo.log('progress url failed ');
30984                 Roo.log(data);
30985             },
30986             scope : this
30987         });
30988            
30989     },
30990     
30991     
30992     run : function()
30993     {
30994         // run get Values on the form, so it syncs any secondary forms.
30995         this.form.getValues();
30996         
30997         var o = this.options;
30998         var method = this.getMethod();
30999         var isPost = method == 'POST';
31000         if(o.clientValidation === false || this.form.isValid()){
31001             
31002             if (this.form.progressUrl) {
31003                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
31004                     (new Date() * 1) + '' + Math.random());
31005                     
31006             } 
31007             
31008             
31009             Roo.Ajax.request(Roo.apply(this.createCallback(), {
31010                 form:this.form.el.dom,
31011                 url:this.getUrl(!isPost),
31012                 method: method,
31013                 params:isPost ? this.getParams() : null,
31014                 isUpload: this.form.fileUpload,
31015                 formData : this.form.formData
31016             }));
31017             
31018             this.uploadProgress();
31019
31020         }else if (o.clientValidation !== false){ // client validation failed
31021             this.failureType = Roo.form.Action.CLIENT_INVALID;
31022             this.form.afterAction(this, false);
31023         }
31024     },
31025
31026     success : function(response)
31027     {
31028         this.uploadComplete= true;
31029         if (this.haveProgress) {
31030             Roo.MessageBox.hide();
31031         }
31032         
31033         
31034         var result = this.processResponse(response);
31035         if(result === true || result.success){
31036             this.form.afterAction(this, true);
31037             return;
31038         }
31039         if(result.errors){
31040             this.form.markInvalid(result.errors);
31041             this.failureType = Roo.form.Action.SERVER_INVALID;
31042         }
31043         this.form.afterAction(this, false);
31044     },
31045     failure : function(response)
31046     {
31047         this.uploadComplete= true;
31048         if (this.haveProgress) {
31049             Roo.MessageBox.hide();
31050         }
31051         
31052         this.response = response;
31053         this.failureType = Roo.form.Action.CONNECT_FAILURE;
31054         this.form.afterAction(this, false);
31055     },
31056     
31057     handleResponse : function(response){
31058         if(this.form.errorReader){
31059             var rs = this.form.errorReader.read(response);
31060             var errors = [];
31061             if(rs.records){
31062                 for(var i = 0, len = rs.records.length; i < len; i++) {
31063                     var r = rs.records[i];
31064                     errors[i] = r.data;
31065                 }
31066             }
31067             if(errors.length < 1){
31068                 errors = null;
31069             }
31070             return {
31071                 success : rs.success,
31072                 errors : errors
31073             };
31074         }
31075         var ret = false;
31076         try {
31077             var rt = response.responseText;
31078             if (rt.match(/^\<!--\[CDATA\[/)) {
31079                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
31080                 rt = rt.replace(/\]\]--\>$/,'');
31081             }
31082             
31083             ret = Roo.decode(rt);
31084         } catch (e) {
31085             ret = {
31086                 success: false,
31087                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
31088                 errors : []
31089             };
31090         }
31091         return ret;
31092         
31093     }
31094 });
31095
31096
31097 Roo.form.Action.Load = function(form, options){
31098     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
31099     this.reader = this.form.reader;
31100 };
31101
31102 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
31103     type : 'load',
31104
31105     run : function(){
31106         
31107         Roo.Ajax.request(Roo.apply(
31108                 this.createCallback(), {
31109                     method:this.getMethod(),
31110                     url:this.getUrl(false),
31111                     params:this.getParams()
31112         }));
31113     },
31114
31115     success : function(response){
31116         
31117         var result = this.processResponse(response);
31118         if(result === true || !result.success || !result.data){
31119             this.failureType = Roo.form.Action.LOAD_FAILURE;
31120             this.form.afterAction(this, false);
31121             return;
31122         }
31123         this.form.clearInvalid();
31124         this.form.setValues(result.data);
31125         this.form.afterAction(this, true);
31126     },
31127
31128     handleResponse : function(response){
31129         if(this.form.reader){
31130             var rs = this.form.reader.read(response);
31131             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
31132             return {
31133                 success : rs.success,
31134                 data : data
31135             };
31136         }
31137         return Roo.decode(response.responseText);
31138     }
31139 });
31140
31141 Roo.form.Action.ACTION_TYPES = {
31142     'load' : Roo.form.Action.Load,
31143     'submit' : Roo.form.Action.Submit
31144 };/*
31145  * Based on:
31146  * Ext JS Library 1.1.1
31147  * Copyright(c) 2006-2007, Ext JS, LLC.
31148  *
31149  * Originally Released Under LGPL - original licence link has changed is not relivant.
31150  *
31151  * Fork - LGPL
31152  * <script type="text/javascript">
31153  */
31154  
31155 /**
31156  * @class Roo.form.Layout
31157  * @extends Roo.Component
31158  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
31159  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
31160  * @constructor
31161  * @param {Object} config Configuration options
31162  */
31163 Roo.form.Layout = function(config){
31164     var xitems = [];
31165     if (config.items) {
31166         xitems = config.items;
31167         delete config.items;
31168     }
31169     Roo.form.Layout.superclass.constructor.call(this, config);
31170     this.stack = [];
31171     Roo.each(xitems, this.addxtype, this);
31172      
31173 };
31174
31175 Roo.extend(Roo.form.Layout, Roo.Component, {
31176     /**
31177      * @cfg {String/Object} autoCreate
31178      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
31179      */
31180     /**
31181      * @cfg {String/Object/Function} style
31182      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
31183      * a function which returns such a specification.
31184      */
31185     /**
31186      * @cfg {String} labelAlign (left|top|right)
31187      * Valid values are "left," "top" and "right" (defaults to "left")
31188      */
31189     /**
31190      * @cfg {Number} labelWidth
31191      * Fixed width in pixels of all field labels (defaults to undefined)
31192      */
31193     /**
31194      * @cfg {Boolean} clear
31195      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
31196      */
31197     clear : true,
31198     /**
31199      * @cfg {String} labelSeparator
31200      * The separator to use after field labels (defaults to ':')
31201      */
31202     labelSeparator : ':',
31203     /**
31204      * @cfg {Boolean} hideLabels
31205      * True to suppress the display of field labels in this layout (defaults to false)
31206      */
31207     hideLabels : false,
31208
31209     // private
31210     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
31211     
31212     isLayout : true,
31213     
31214     // private
31215     onRender : function(ct, position){
31216         if(this.el){ // from markup
31217             this.el = Roo.get(this.el);
31218         }else {  // generate
31219             var cfg = this.getAutoCreate();
31220             this.el = ct.createChild(cfg, position);
31221         }
31222         if(this.style){
31223             this.el.applyStyles(this.style);
31224         }
31225         if(this.labelAlign){
31226             this.el.addClass('x-form-label-'+this.labelAlign);
31227         }
31228         if(this.hideLabels){
31229             this.labelStyle = "display:none";
31230             this.elementStyle = "padding-left:0;";
31231         }else{
31232             if(typeof this.labelWidth == 'number'){
31233                 this.labelStyle = "width:"+this.labelWidth+"px;";
31234                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
31235             }
31236             if(this.labelAlign == 'top'){
31237                 this.labelStyle = "width:auto;";
31238                 this.elementStyle = "padding-left:0;";
31239             }
31240         }
31241         var stack = this.stack;
31242         var slen = stack.length;
31243         if(slen > 0){
31244             if(!this.fieldTpl){
31245                 var t = new Roo.Template(
31246                     '<div class="x-form-item {5}">',
31247                         '<label for="{0}" style="{2}">{1}{4}</label>',
31248                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
31249                         '</div>',
31250                     '</div><div class="x-form-clear-left"></div>'
31251                 );
31252                 t.disableFormats = true;
31253                 t.compile();
31254                 Roo.form.Layout.prototype.fieldTpl = t;
31255             }
31256             for(var i = 0; i < slen; i++) {
31257                 if(stack[i].isFormField){
31258                     this.renderField(stack[i]);
31259                 }else{
31260                     this.renderComponent(stack[i]);
31261                 }
31262             }
31263         }
31264         if(this.clear){
31265             this.el.createChild({cls:'x-form-clear'});
31266         }
31267     },
31268
31269     // private
31270     renderField : function(f){
31271         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
31272                f.id, //0
31273                f.fieldLabel, //1
31274                f.labelStyle||this.labelStyle||'', //2
31275                this.elementStyle||'', //3
31276                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
31277                f.itemCls||this.itemCls||''  //5
31278        ], true).getPrevSibling());
31279     },
31280
31281     // private
31282     renderComponent : function(c){
31283         c.render(c.isLayout ? this.el : this.el.createChild());    
31284     },
31285     /**
31286      * Adds a object form elements (using the xtype property as the factory method.)
31287      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
31288      * @param {Object} config 
31289      */
31290     addxtype : function(o)
31291     {
31292         // create the lement.
31293         o.form = this.form;
31294         var fe = Roo.factory(o, Roo.form);
31295         this.form.allItems.push(fe);
31296         this.stack.push(fe);
31297         
31298         if (fe.isFormField) {
31299             this.form.items.add(fe);
31300         }
31301          
31302         return fe;
31303     }
31304 });
31305
31306
31307 /**
31308  * @class Roo.form.Column
31309  * @extends Roo.form.Layout
31310  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
31311  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
31312  * @constructor
31313  * @param {Object} config Configuration options
31314  */
31315 Roo.form.Column = function(config){
31316     Roo.form.Column.superclass.constructor.call(this, config);
31317 };
31318
31319 Roo.extend(Roo.form.Column, Roo.form.Layout, {
31320     /**
31321      * @cfg {Number/String} width
31322      * The fixed width of the column in pixels or CSS value (defaults to "auto")
31323      */
31324     /**
31325      * @cfg {String/Object} autoCreate
31326      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
31327      */
31328
31329     // private
31330     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
31331
31332     // private
31333     onRender : function(ct, position){
31334         Roo.form.Column.superclass.onRender.call(this, ct, position);
31335         if(this.width){
31336             this.el.setWidth(this.width);
31337         }
31338     }
31339 });
31340
31341 /**
31342  * @class Roo.form.Row
31343  * @extends Roo.form.Layout
31344  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
31345  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
31346  * @constructor
31347  * @param {Object} config Configuration options
31348  */
31349
31350  
31351 Roo.form.Row = function(config){
31352     Roo.form.Row.superclass.constructor.call(this, config);
31353 };
31354  
31355 Roo.extend(Roo.form.Row, Roo.form.Layout, {
31356       /**
31357      * @cfg {Number/String} width
31358      * The fixed width of the column in pixels or CSS value (defaults to "auto")
31359      */
31360     /**
31361      * @cfg {Number/String} height
31362      * The fixed height of the column in pixels or CSS value (defaults to "auto")
31363      */
31364     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
31365     
31366     padWidth : 20,
31367     // private
31368     onRender : function(ct, position){
31369         //console.log('row render');
31370         if(!this.rowTpl){
31371             var t = new Roo.Template(
31372                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
31373                     '<label for="{0}" style="{2}">{1}{4}</label>',
31374                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
31375                     '</div>',
31376                 '</div>'
31377             );
31378             t.disableFormats = true;
31379             t.compile();
31380             Roo.form.Layout.prototype.rowTpl = t;
31381         }
31382         this.fieldTpl = this.rowTpl;
31383         
31384         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
31385         var labelWidth = 100;
31386         
31387         if ((this.labelAlign != 'top')) {
31388             if (typeof this.labelWidth == 'number') {
31389                 labelWidth = this.labelWidth
31390             }
31391             this.padWidth =  20 + labelWidth;
31392             
31393         }
31394         
31395         Roo.form.Column.superclass.onRender.call(this, ct, position);
31396         if(this.width){
31397             this.el.setWidth(this.width);
31398         }
31399         if(this.height){
31400             this.el.setHeight(this.height);
31401         }
31402     },
31403     
31404     // private
31405     renderField : function(f){
31406         f.fieldEl = this.fieldTpl.append(this.el, [
31407                f.id, f.fieldLabel,
31408                f.labelStyle||this.labelStyle||'',
31409                this.elementStyle||'',
31410                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
31411                f.itemCls||this.itemCls||'',
31412                f.width ? f.width + this.padWidth : 160 + this.padWidth
31413        ],true);
31414     }
31415 });
31416  
31417
31418 /**
31419  * @class Roo.form.FieldSet
31420  * @extends Roo.form.Layout
31421  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
31422  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
31423  * @constructor
31424  * @param {Object} config Configuration options
31425  */
31426 Roo.form.FieldSet = function(config){
31427     Roo.form.FieldSet.superclass.constructor.call(this, config);
31428 };
31429
31430 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
31431     /**
31432      * @cfg {String} legend
31433      * The text to display as the legend for the FieldSet (defaults to '')
31434      */
31435     /**
31436      * @cfg {String/Object} autoCreate
31437      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
31438      */
31439
31440     // private
31441     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
31442
31443     // private
31444     onRender : function(ct, position){
31445         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
31446         if(this.legend){
31447             this.setLegend(this.legend);
31448         }
31449     },
31450
31451     // private
31452     setLegend : function(text){
31453         if(this.rendered){
31454             this.el.child('legend').update(text);
31455         }
31456     }
31457 });/*
31458  * Based on:
31459  * Ext JS Library 1.1.1
31460  * Copyright(c) 2006-2007, Ext JS, LLC.
31461  *
31462  * Originally Released Under LGPL - original licence link has changed is not relivant.
31463  *
31464  * Fork - LGPL
31465  * <script type="text/javascript">
31466  */
31467 /**
31468  * @class Roo.form.VTypes
31469  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
31470  * @static
31471  */
31472 Roo.form.VTypes = function(){
31473     // closure these in so they are only created once.
31474     var alpha = /^[a-zA-Z_]+$/;
31475     var alphanum = /^[a-zA-Z0-9_]+$/;
31476     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
31477     var url = /^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
31478     var urlWeb = /^((https?):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
31479
31480     // All these messages and functions are configurable
31481     return {
31482         /**
31483          * The function used to validate email addresses
31484          * @param {String} value The email address
31485          */
31486         email : function(v){
31487             return email.test(v);
31488         },
31489         /**
31490          * The error text to display when the email validation function returns false
31491          * @type String
31492          */
31493         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
31494         /**
31495          * The keystroke filter mask to be applied on email input
31496          * @type RegExp
31497          */
31498         emailMask : /[a-z0-9_\.\-@]/i,
31499
31500         /**
31501          * The function used to validate URLs
31502          * @param {String} value The URL
31503          */
31504         url : function(v){
31505             return url.test(v);
31506         },
31507         /**
31508          * The funciton used to validate URLs (only allow schemes 'https' and 'http')
31509          * @param {String} v The URL
31510          */
31511         urlWeb : function(v) {
31512             return urlWeb.test(v);
31513         },
31514         /**
31515          * The error text to display when the url validation function returns false
31516          * @type String
31517          */
31518         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
31519         
31520         /**
31521          * The function used to validate alpha values
31522          * @param {String} value The value
31523          */
31524         alpha : function(v){
31525             return alpha.test(v);
31526         },
31527         /**
31528          * The error text to display when the alpha validation function returns false
31529          * @type String
31530          */
31531         alphaText : 'This field should only contain letters and _',
31532         /**
31533          * The keystroke filter mask to be applied on alpha input
31534          * @type RegExp
31535          */
31536         alphaMask : /[a-z_]/i,
31537
31538         /**
31539          * The function used to validate alphanumeric values
31540          * @param {String} value The value
31541          */
31542         alphanum : function(v){
31543             return alphanum.test(v);
31544         },
31545         /**
31546          * The error text to display when the alphanumeric validation function returns false
31547          * @type String
31548          */
31549         alphanumText : 'This field should only contain letters, numbers and _',
31550         /**
31551          * The keystroke filter mask to be applied on alphanumeric input
31552          * @type RegExp
31553          */
31554         alphanumMask : /[a-z0-9_]/i
31555     };
31556 }();//<script type="text/javascript">
31557
31558 /**
31559  * @class Roo.form.FCKeditor
31560  * @extends Roo.form.TextArea
31561  * Wrapper around the FCKEditor http://www.fckeditor.net
31562  * @constructor
31563  * Creates a new FCKeditor
31564  * @param {Object} config Configuration options
31565  */
31566 Roo.form.FCKeditor = function(config){
31567     Roo.form.FCKeditor.superclass.constructor.call(this, config);
31568     this.addEvents({
31569          /**
31570          * @event editorinit
31571          * Fired when the editor is initialized - you can add extra handlers here..
31572          * @param {FCKeditor} this
31573          * @param {Object} the FCK object.
31574          */
31575         editorinit : true
31576     });
31577     
31578     
31579 };
31580 Roo.form.FCKeditor.editors = { };
31581 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
31582 {
31583     //defaultAutoCreate : {
31584     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
31585     //},
31586     // private
31587     /**
31588      * @cfg {Object} fck options - see fck manual for details.
31589      */
31590     fckconfig : false,
31591     
31592     /**
31593      * @cfg {Object} fck toolbar set (Basic or Default)
31594      */
31595     toolbarSet : 'Basic',
31596     /**
31597      * @cfg {Object} fck BasePath
31598      */ 
31599     basePath : '/fckeditor/',
31600     
31601     
31602     frame : false,
31603     
31604     value : '',
31605     
31606    
31607     onRender : function(ct, position)
31608     {
31609         if(!this.el){
31610             this.defaultAutoCreate = {
31611                 tag: "textarea",
31612                 style:"width:300px;height:60px;",
31613                 autocomplete: "new-password"
31614             };
31615         }
31616         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
31617         /*
31618         if(this.grow){
31619             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
31620             if(this.preventScrollbars){
31621                 this.el.setStyle("overflow", "hidden");
31622             }
31623             this.el.setHeight(this.growMin);
31624         }
31625         */
31626         //console.log('onrender' + this.getId() );
31627         Roo.form.FCKeditor.editors[this.getId()] = this;
31628          
31629
31630         this.replaceTextarea() ;
31631         
31632     },
31633     
31634     getEditor : function() {
31635         return this.fckEditor;
31636     },
31637     /**
31638      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
31639      * @param {Mixed} value The value to set
31640      */
31641     
31642     
31643     setValue : function(value)
31644     {
31645         //console.log('setValue: ' + value);
31646         
31647         if(typeof(value) == 'undefined') { // not sure why this is happending...
31648             return;
31649         }
31650         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31651         
31652         //if(!this.el || !this.getEditor()) {
31653         //    this.value = value;
31654             //this.setValue.defer(100,this,[value]);    
31655         //    return;
31656         //} 
31657         
31658         if(!this.getEditor()) {
31659             return;
31660         }
31661         
31662         this.getEditor().SetData(value);
31663         
31664         //
31665
31666     },
31667
31668     /**
31669      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
31670      * @return {Mixed} value The field value
31671      */
31672     getValue : function()
31673     {
31674         
31675         if (this.frame && this.frame.dom.style.display == 'none') {
31676             return Roo.form.FCKeditor.superclass.getValue.call(this);
31677         }
31678         
31679         if(!this.el || !this.getEditor()) {
31680            
31681            // this.getValue.defer(100,this); 
31682             return this.value;
31683         }
31684        
31685         
31686         var value=this.getEditor().GetData();
31687         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31688         return Roo.form.FCKeditor.superclass.getValue.call(this);
31689         
31690
31691     },
31692
31693     /**
31694      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
31695      * @return {Mixed} value The field value
31696      */
31697     getRawValue : function()
31698     {
31699         if (this.frame && this.frame.dom.style.display == 'none') {
31700             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31701         }
31702         
31703         if(!this.el || !this.getEditor()) {
31704             //this.getRawValue.defer(100,this); 
31705             return this.value;
31706             return;
31707         }
31708         
31709         
31710         
31711         var value=this.getEditor().GetData();
31712         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31713         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31714          
31715     },
31716     
31717     setSize : function(w,h) {
31718         
31719         
31720         
31721         //if (this.frame && this.frame.dom.style.display == 'none') {
31722         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31723         //    return;
31724         //}
31725         //if(!this.el || !this.getEditor()) {
31726         //    this.setSize.defer(100,this, [w,h]); 
31727         //    return;
31728         //}
31729         
31730         
31731         
31732         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31733         
31734         this.frame.dom.setAttribute('width', w);
31735         this.frame.dom.setAttribute('height', h);
31736         this.frame.setSize(w,h);
31737         
31738     },
31739     
31740     toggleSourceEdit : function(value) {
31741         
31742       
31743          
31744         this.el.dom.style.display = value ? '' : 'none';
31745         this.frame.dom.style.display = value ?  'none' : '';
31746         
31747     },
31748     
31749     
31750     focus: function(tag)
31751     {
31752         if (this.frame.dom.style.display == 'none') {
31753             return Roo.form.FCKeditor.superclass.focus.call(this);
31754         }
31755         if(!this.el || !this.getEditor()) {
31756             this.focus.defer(100,this, [tag]); 
31757             return;
31758         }
31759         
31760         
31761         
31762         
31763         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31764         this.getEditor().Focus();
31765         if (tgs.length) {
31766             if (!this.getEditor().Selection.GetSelection()) {
31767                 this.focus.defer(100,this, [tag]); 
31768                 return;
31769             }
31770             
31771             
31772             var r = this.getEditor().EditorDocument.createRange();
31773             r.setStart(tgs[0],0);
31774             r.setEnd(tgs[0],0);
31775             this.getEditor().Selection.GetSelection().removeAllRanges();
31776             this.getEditor().Selection.GetSelection().addRange(r);
31777             this.getEditor().Focus();
31778         }
31779         
31780     },
31781     
31782     
31783     
31784     replaceTextarea : function()
31785     {
31786         if ( document.getElementById( this.getId() + '___Frame' ) ) {
31787             return ;
31788         }
31789         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31790         //{
31791             // We must check the elements firstly using the Id and then the name.
31792         var oTextarea = document.getElementById( this.getId() );
31793         
31794         var colElementsByName = document.getElementsByName( this.getId() ) ;
31795          
31796         oTextarea.style.display = 'none' ;
31797
31798         if ( oTextarea.tabIndex ) {            
31799             this.TabIndex = oTextarea.tabIndex ;
31800         }
31801         
31802         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31803         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31804         this.frame = Roo.get(this.getId() + '___Frame')
31805     },
31806     
31807     _getConfigHtml : function()
31808     {
31809         var sConfig = '' ;
31810
31811         for ( var o in this.fckconfig ) {
31812             sConfig += sConfig.length > 0  ? '&amp;' : '';
31813             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31814         }
31815
31816         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31817     },
31818     
31819     
31820     _getIFrameHtml : function()
31821     {
31822         var sFile = 'fckeditor.html' ;
31823         /* no idea what this is about..
31824         try
31825         {
31826             if ( (/fcksource=true/i).test( window.top.location.search ) )
31827                 sFile = 'fckeditor.original.html' ;
31828         }
31829         catch (e) { 
31830         */
31831
31832         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31833         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
31834         
31835         
31836         var html = '<iframe id="' + this.getId() +
31837             '___Frame" src="' + sLink +
31838             '" width="' + this.width +
31839             '" height="' + this.height + '"' +
31840             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
31841             ' frameborder="0" scrolling="no"></iframe>' ;
31842
31843         return html ;
31844     },
31845     
31846     _insertHtmlBefore : function( html, element )
31847     {
31848         if ( element.insertAdjacentHTML )       {
31849             // IE
31850             element.insertAdjacentHTML( 'beforeBegin', html ) ;
31851         } else { // Gecko
31852             var oRange = document.createRange() ;
31853             oRange.setStartBefore( element ) ;
31854             var oFragment = oRange.createContextualFragment( html );
31855             element.parentNode.insertBefore( oFragment, element ) ;
31856         }
31857     }
31858     
31859     
31860   
31861     
31862     
31863     
31864     
31865
31866 });
31867
31868 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31869
31870 function FCKeditor_OnComplete(editorInstance){
31871     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31872     f.fckEditor = editorInstance;
31873     //console.log("loaded");
31874     f.fireEvent('editorinit', f, editorInstance);
31875
31876   
31877
31878  
31879
31880
31881
31882
31883
31884
31885
31886
31887
31888
31889
31890
31891
31892
31893
31894 //<script type="text/javascript">
31895 /**
31896  * @class Roo.form.GridField
31897  * @extends Roo.form.Field
31898  * Embed a grid (or editable grid into a form)
31899  * STATUS ALPHA
31900  * 
31901  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31902  * it needs 
31903  * xgrid.store = Roo.data.Store
31904  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31905  * xgrid.store.reader = Roo.data.JsonReader 
31906  * 
31907  * 
31908  * @constructor
31909  * Creates a new GridField
31910  * @param {Object} config Configuration options
31911  */
31912 Roo.form.GridField = function(config){
31913     Roo.form.GridField.superclass.constructor.call(this, config);
31914      
31915 };
31916
31917 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
31918     /**
31919      * @cfg {Number} width  - used to restrict width of grid..
31920      */
31921     width : 100,
31922     /**
31923      * @cfg {Number} height - used to restrict height of grid..
31924      */
31925     height : 50,
31926      /**
31927      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31928          * 
31929          *}
31930      */
31931     xgrid : false, 
31932     /**
31933      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31934      * {tag: "input", type: "checkbox", autocomplete: "off"})
31935      */
31936    // defaultAutoCreate : { tag: 'div' },
31937     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31938     /**
31939      * @cfg {String} addTitle Text to include for adding a title.
31940      */
31941     addTitle : false,
31942     //
31943     onResize : function(){
31944         Roo.form.Field.superclass.onResize.apply(this, arguments);
31945     },
31946
31947     initEvents : function(){
31948         // Roo.form.Checkbox.superclass.initEvents.call(this);
31949         // has no events...
31950        
31951     },
31952
31953
31954     getResizeEl : function(){
31955         return this.wrap;
31956     },
31957
31958     getPositionEl : function(){
31959         return this.wrap;
31960     },
31961
31962     // private
31963     onRender : function(ct, position){
31964         
31965         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31966         var style = this.style;
31967         delete this.style;
31968         
31969         Roo.form.GridField.superclass.onRender.call(this, ct, position);
31970         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31971         this.viewEl = this.wrap.createChild({ tag: 'div' });
31972         if (style) {
31973             this.viewEl.applyStyles(style);
31974         }
31975         if (this.width) {
31976             this.viewEl.setWidth(this.width);
31977         }
31978         if (this.height) {
31979             this.viewEl.setHeight(this.height);
31980         }
31981         //if(this.inputValue !== undefined){
31982         //this.setValue(this.value);
31983         
31984         
31985         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31986         
31987         
31988         this.grid.render();
31989         this.grid.getDataSource().on('remove', this.refreshValue, this);
31990         this.grid.getDataSource().on('update', this.refreshValue, this);
31991         this.grid.on('afteredit', this.refreshValue, this);
31992  
31993     },
31994      
31995     
31996     /**
31997      * Sets the value of the item. 
31998      * @param {String} either an object  or a string..
31999      */
32000     setValue : function(v){
32001         //this.value = v;
32002         v = v || []; // empty set..
32003         // this does not seem smart - it really only affects memoryproxy grids..
32004         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
32005             var ds = this.grid.getDataSource();
32006             // assumes a json reader..
32007             var data = {}
32008             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
32009             ds.loadData( data);
32010         }
32011         // clear selection so it does not get stale.
32012         if (this.grid.sm) { 
32013             this.grid.sm.clearSelections();
32014         }
32015         
32016         Roo.form.GridField.superclass.setValue.call(this, v);
32017         this.refreshValue();
32018         // should load data in the grid really....
32019     },
32020     
32021     // private
32022     refreshValue: function() {
32023          var val = [];
32024         this.grid.getDataSource().each(function(r) {
32025             val.push(r.data);
32026         });
32027         this.el.dom.value = Roo.encode(val);
32028     }
32029     
32030      
32031     
32032     
32033 });/*
32034  * Based on:
32035  * Ext JS Library 1.1.1
32036  * Copyright(c) 2006-2007, Ext JS, LLC.
32037  *
32038  * Originally Released Under LGPL - original licence link has changed is not relivant.
32039  *
32040  * Fork - LGPL
32041  * <script type="text/javascript">
32042  */
32043 /**
32044  * @class Roo.form.DisplayField
32045  * @extends Roo.form.Field
32046  * A generic Field to display non-editable data.
32047  * @cfg {Boolean} closable (true|false) default false
32048  * @constructor
32049  * Creates a new Display Field item.
32050  * @param {Object} config Configuration options
32051  */
32052 Roo.form.DisplayField = function(config){
32053     Roo.form.DisplayField.superclass.constructor.call(this, config);
32054     
32055     this.addEvents({
32056         /**
32057          * @event close
32058          * Fires after the click the close btn
32059              * @param {Roo.form.DisplayField} this
32060              */
32061         close : true
32062     });
32063 };
32064
32065 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
32066     inputType:      'hidden',
32067     allowBlank:     true,
32068     readOnly:         true,
32069     
32070  
32071     /**
32072      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
32073      */
32074     focusClass : undefined,
32075     /**
32076      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
32077      */
32078     fieldClass: 'x-form-field',
32079     
32080      /**
32081      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
32082      */
32083     valueRenderer: undefined,
32084     
32085     width: 100,
32086     /**
32087      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
32088      * {tag: "input", type: "checkbox", autocomplete: "off"})
32089      */
32090      
32091  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
32092  
32093     closable : false,
32094     
32095     onResize : function(){
32096         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
32097         
32098     },
32099
32100     initEvents : function(){
32101         // Roo.form.Checkbox.superclass.initEvents.call(this);
32102         // has no events...
32103         
32104         if(this.closable){
32105             this.closeEl.on('click', this.onClose, this);
32106         }
32107        
32108     },
32109
32110
32111     getResizeEl : function(){
32112         return this.wrap;
32113     },
32114
32115     getPositionEl : function(){
32116         return this.wrap;
32117     },
32118
32119     // private
32120     onRender : function(ct, position){
32121         
32122         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
32123         //if(this.inputValue !== undefined){
32124         this.wrap = this.el.wrap();
32125         
32126         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
32127         
32128         if(this.closable){
32129             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
32130         }
32131         
32132         if (this.bodyStyle) {
32133             this.viewEl.applyStyles(this.bodyStyle);
32134         }
32135         //this.viewEl.setStyle('padding', '2px');
32136         
32137         this.setValue(this.value);
32138         
32139     },
32140 /*
32141     // private
32142     initValue : Roo.emptyFn,
32143
32144   */
32145
32146         // private
32147     onClick : function(){
32148         
32149     },
32150
32151     /**
32152      * Sets the checked state of the checkbox.
32153      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
32154      */
32155     setValue : function(v){
32156         this.value = v;
32157         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
32158         // this might be called before we have a dom element..
32159         if (!this.viewEl) {
32160             return;
32161         }
32162         this.viewEl.dom.innerHTML = html;
32163         Roo.form.DisplayField.superclass.setValue.call(this, v);
32164
32165     },
32166     
32167     onClose : function(e)
32168     {
32169         e.preventDefault();
32170         
32171         this.fireEvent('close', this);
32172     }
32173 });/*
32174  * 
32175  * Licence- LGPL
32176  * 
32177  */
32178
32179 /**
32180  * @class Roo.form.DayPicker
32181  * @extends Roo.form.Field
32182  * A Day picker show [M] [T] [W] ....
32183  * @constructor
32184  * Creates a new Day Picker
32185  * @param {Object} config Configuration options
32186  */
32187 Roo.form.DayPicker= function(config){
32188     Roo.form.DayPicker.superclass.constructor.call(this, config);
32189      
32190 };
32191
32192 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
32193     /**
32194      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
32195      */
32196     focusClass : undefined,
32197     /**
32198      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
32199      */
32200     fieldClass: "x-form-field",
32201    
32202     /**
32203      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
32204      * {tag: "input", type: "checkbox", autocomplete: "off"})
32205      */
32206     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
32207     
32208    
32209     actionMode : 'viewEl', 
32210     //
32211     // private
32212  
32213     inputType : 'hidden',
32214     
32215      
32216     inputElement: false, // real input element?
32217     basedOn: false, // ????
32218     
32219     isFormField: true, // not sure where this is needed!!!!
32220
32221     onResize : function(){
32222         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
32223         if(!this.boxLabel){
32224             this.el.alignTo(this.wrap, 'c-c');
32225         }
32226     },
32227
32228     initEvents : function(){
32229         Roo.form.Checkbox.superclass.initEvents.call(this);
32230         this.el.on("click", this.onClick,  this);
32231         this.el.on("change", this.onClick,  this);
32232     },
32233
32234
32235     getResizeEl : function(){
32236         return this.wrap;
32237     },
32238
32239     getPositionEl : function(){
32240         return this.wrap;
32241     },
32242
32243     
32244     // private
32245     onRender : function(ct, position){
32246         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
32247        
32248         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
32249         
32250         var r1 = '<table><tr>';
32251         var r2 = '<tr class="x-form-daypick-icons">';
32252         for (var i=0; i < 7; i++) {
32253             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
32254             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
32255         }
32256         
32257         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
32258         viewEl.select('img').on('click', this.onClick, this);
32259         this.viewEl = viewEl;   
32260         
32261         
32262         // this will not work on Chrome!!!
32263         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
32264         this.el.on('propertychange', this.setFromHidden,  this);  //ie
32265         
32266         
32267           
32268
32269     },
32270
32271     // private
32272     initValue : Roo.emptyFn,
32273
32274     /**
32275      * Returns the checked state of the checkbox.
32276      * @return {Boolean} True if checked, else false
32277      */
32278     getValue : function(){
32279         return this.el.dom.value;
32280         
32281     },
32282
32283         // private
32284     onClick : function(e){ 
32285         //this.setChecked(!this.checked);
32286         Roo.get(e.target).toggleClass('x-menu-item-checked');
32287         this.refreshValue();
32288         //if(this.el.dom.checked != this.checked){
32289         //    this.setValue(this.el.dom.checked);
32290        // }
32291     },
32292     
32293     // private
32294     refreshValue : function()
32295     {
32296         var val = '';
32297         this.viewEl.select('img',true).each(function(e,i,n)  {
32298             val += e.is(".x-menu-item-checked") ? String(n) : '';
32299         });
32300         this.setValue(val, true);
32301     },
32302
32303     /**
32304      * Sets the checked state of the checkbox.
32305      * On is always based on a string comparison between inputValue and the param.
32306      * @param {Boolean/String} value - the value to set 
32307      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
32308      */
32309     setValue : function(v,suppressEvent){
32310         if (!this.el.dom) {
32311             return;
32312         }
32313         var old = this.el.dom.value ;
32314         this.el.dom.value = v;
32315         if (suppressEvent) {
32316             return ;
32317         }
32318          
32319         // update display..
32320         this.viewEl.select('img',true).each(function(e,i,n)  {
32321             
32322             var on = e.is(".x-menu-item-checked");
32323             var newv = v.indexOf(String(n)) > -1;
32324             if (on != newv) {
32325                 e.toggleClass('x-menu-item-checked');
32326             }
32327             
32328         });
32329         
32330         
32331         this.fireEvent('change', this, v, old);
32332         
32333         
32334     },
32335    
32336     // handle setting of hidden value by some other method!!?!?
32337     setFromHidden: function()
32338     {
32339         if(!this.el){
32340             return;
32341         }
32342         //console.log("SET FROM HIDDEN");
32343         //alert('setFrom hidden');
32344         this.setValue(this.el.dom.value);
32345     },
32346     
32347     onDestroy : function()
32348     {
32349         if(this.viewEl){
32350             Roo.get(this.viewEl).remove();
32351         }
32352          
32353         Roo.form.DayPicker.superclass.onDestroy.call(this);
32354     }
32355
32356 });/*
32357  * RooJS Library 1.1.1
32358  * Copyright(c) 2008-2011  Alan Knowles
32359  *
32360  * License - LGPL
32361  */
32362  
32363
32364 /**
32365  * @class Roo.form.ComboCheck
32366  * @extends Roo.form.ComboBox
32367  * A combobox for multiple select items.
32368  *
32369  * FIXME - could do with a reset button..
32370  * 
32371  * @constructor
32372  * Create a new ComboCheck
32373  * @param {Object} config Configuration options
32374  */
32375 Roo.form.ComboCheck = function(config){
32376     Roo.form.ComboCheck.superclass.constructor.call(this, config);
32377     // should verify some data...
32378     // like
32379     // hiddenName = required..
32380     // displayField = required
32381     // valudField == required
32382     var req= [ 'hiddenName', 'displayField', 'valueField' ];
32383     var _t = this;
32384     Roo.each(req, function(e) {
32385         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
32386             throw "Roo.form.ComboCheck : missing value for: " + e;
32387         }
32388     });
32389     
32390     
32391 };
32392
32393 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
32394      
32395      
32396     editable : false,
32397      
32398     selectedClass: 'x-menu-item-checked', 
32399     
32400     // private
32401     onRender : function(ct, position){
32402         var _t = this;
32403         
32404         
32405         
32406         if(!this.tpl){
32407             var cls = 'x-combo-list';
32408
32409             
32410             this.tpl =  new Roo.Template({
32411                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
32412                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
32413                    '<span>{' + this.displayField + '}</span>' +
32414                     '</div>' 
32415                 
32416             });
32417         }
32418  
32419         
32420         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
32421         this.view.singleSelect = false;
32422         this.view.multiSelect = true;
32423         this.view.toggleSelect = true;
32424         this.pageTb.add(new Roo.Toolbar.Fill(),{
32425             
32426             text: 'Select All',
32427             handler: function() {
32428                 _t.selectAll();
32429             }
32430         },
32431         {
32432             text: 'Done',
32433             handler: function() {
32434                 _t.collapse();
32435             }
32436         });
32437     },
32438     
32439     cleanLeadingSpace : function(e)
32440     {
32441         // this is disabled, as it retriggers setvalue on blur
32442         return;
32443     },
32444     doForce : function() {
32445         // no idea what this did, but it blanks out our values.
32446         return;
32447     },
32448     onViewOver : function(e, t){
32449         // do nothing...
32450         return;
32451         
32452     },
32453     
32454     onViewClick : function(doFocus,index){
32455         return;
32456         
32457     },
32458     select: function () {
32459         //Roo.log("SELECT CALLED");
32460     },
32461      
32462     selectByValue : function(xv, scrollIntoView){
32463         var ar = this.getValueArray();
32464         var sels = [];
32465         
32466         Roo.each(ar, function(v) {
32467             if(v === undefined || v === null){
32468                 return;
32469             }
32470             var r = this.findRecord(this.valueField, v);
32471             if(r){
32472                 sels.push(this.store.indexOf(r))
32473                 
32474             }
32475         },this);
32476         this.view.select(sels);
32477         return false;
32478     },
32479     
32480     selectAll : function()
32481     {
32482         var sels = [];
32483         this.store.each(function(r,i) {
32484             sels.push(i);
32485         });
32486         this.view.select(sels);
32487         this.collapse();
32488         return false;
32489
32490     },
32491     
32492     onSelect : function(record, index){
32493        // Roo.log("onselect Called");
32494        // this is only called by the clear button now..
32495         this.view.clearSelections();
32496         this.setValue('[]');
32497         if (this.value != this.valueBefore) {
32498             this.fireEvent('change', this, this.value, this.valueBefore);
32499             this.valueBefore = this.value;
32500         }
32501     },
32502     getValueArray : function()
32503     {
32504         var ar = [] ;
32505         
32506         try {
32507             //Roo.log(this.value);
32508             if (typeof(this.value) == 'undefined') {
32509                 return [];
32510             }
32511             var ar = Roo.decode(this.value);
32512             return  ar instanceof Array ? ar : []; //?? valid?
32513             
32514         } catch(e) {
32515             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
32516             return [];
32517         }
32518          
32519     },
32520     expand : function ()
32521     {
32522         
32523         Roo.form.ComboCheck.superclass.expand.call(this);
32524         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
32525         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
32526         
32527
32528     },
32529     
32530     collapse : function(){
32531         Roo.form.ComboCheck.superclass.collapse.call(this);
32532         var sl = this.view.getSelectedIndexes();
32533         var st = this.store;
32534         var nv = [];
32535         var tv = [];
32536         var r;
32537         Roo.each(sl, function(i) {
32538             r = st.getAt(i);
32539             nv.push(r.get(this.valueField));
32540         },this);
32541         this.setValue(Roo.encode(nv));
32542         if (this.value != this.valueBefore) {
32543
32544             this.fireEvent('change', this, this.value, this.valueBefore);
32545             this.valueBefore = this.value;
32546         }
32547         
32548     },
32549     
32550     setValue : function(v){
32551         // Roo.log(v);
32552         this.value = v;
32553         
32554         var vals = this.getValueArray();
32555         var tv = [];
32556         Roo.each(vals, function(k) {
32557             var r = this.findRecord(this.valueField, k);
32558             if(r){
32559                 tv.push(r.data[this.displayField]);
32560             }else if(this.valueNotFoundText !== undefined){
32561                 tv.push( this.valueNotFoundText );
32562             }
32563         },this);
32564        // Roo.log(tv);
32565         
32566         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
32567         this.hiddenField.value = v;
32568         this.value = v;
32569     }
32570     
32571 });/*
32572  * Based on:
32573  * Ext JS Library 1.1.1
32574  * Copyright(c) 2006-2007, Ext JS, LLC.
32575  *
32576  * Originally Released Under LGPL - original licence link has changed is not relivant.
32577  *
32578  * Fork - LGPL
32579  * <script type="text/javascript">
32580  */
32581  
32582 /**
32583  * @class Roo.form.Signature
32584  * @extends Roo.form.Field
32585  * Signature field.  
32586  * @constructor
32587  * 
32588  * @param {Object} config Configuration options
32589  */
32590
32591 Roo.form.Signature = function(config){
32592     Roo.form.Signature.superclass.constructor.call(this, config);
32593     
32594     this.addEvents({// not in used??
32595          /**
32596          * @event confirm
32597          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
32598              * @param {Roo.form.Signature} combo This combo box
32599              */
32600         'confirm' : true,
32601         /**
32602          * @event reset
32603          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
32604              * @param {Roo.form.ComboBox} combo This combo box
32605              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
32606              */
32607         'reset' : true
32608     });
32609 };
32610
32611 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
32612     /**
32613      * @cfg {Object} labels Label to use when rendering a form.
32614      * defaults to 
32615      * labels : { 
32616      *      clear : "Clear",
32617      *      confirm : "Confirm"
32618      *  }
32619      */
32620     labels : { 
32621         clear : "Clear",
32622         confirm : "Confirm"
32623     },
32624     /**
32625      * @cfg {Number} width The signature panel width (defaults to 300)
32626      */
32627     width: 300,
32628     /**
32629      * @cfg {Number} height The signature panel height (defaults to 100)
32630      */
32631     height : 100,
32632     /**
32633      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
32634      */
32635     allowBlank : false,
32636     
32637     //private
32638     // {Object} signPanel The signature SVG panel element (defaults to {})
32639     signPanel : {},
32640     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
32641     isMouseDown : false,
32642     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
32643     isConfirmed : false,
32644     // {String} signatureTmp SVG mapping string (defaults to empty string)
32645     signatureTmp : '',
32646     
32647     
32648     defaultAutoCreate : { // modified by initCompnoent..
32649         tag: "input",
32650         type:"hidden"
32651     },
32652
32653     // private
32654     onRender : function(ct, position){
32655         
32656         Roo.form.Signature.superclass.onRender.call(this, ct, position);
32657         
32658         this.wrap = this.el.wrap({
32659             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32660         });
32661         
32662         this.createToolbar(this);
32663         this.signPanel = this.wrap.createChild({
32664                 tag: 'div',
32665                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32666             }, this.el
32667         );
32668             
32669         this.svgID = Roo.id();
32670         this.svgEl = this.signPanel.createChild({
32671               xmlns : 'http://www.w3.org/2000/svg',
32672               tag : 'svg',
32673               id : this.svgID + "-svg",
32674               width: this.width,
32675               height: this.height,
32676               viewBox: '0 0 '+this.width+' '+this.height,
32677               cn : [
32678                 {
32679                     tag: "rect",
32680                     id: this.svgID + "-svg-r",
32681                     width: this.width,
32682                     height: this.height,
32683                     fill: "#ffa"
32684                 },
32685                 {
32686                     tag: "line",
32687                     id: this.svgID + "-svg-l",
32688                     x1: "0", // start
32689                     y1: (this.height*0.8), // start set the line in 80% of height
32690                     x2: this.width, // end
32691                     y2: (this.height*0.8), // end set the line in 80% of height
32692                     'stroke': "#666",
32693                     'stroke-width': "1",
32694                     'stroke-dasharray': "3",
32695                     'shape-rendering': "crispEdges",
32696                     'pointer-events': "none"
32697                 },
32698                 {
32699                     tag: "path",
32700                     id: this.svgID + "-svg-p",
32701                     'stroke': "navy",
32702                     'stroke-width': "3",
32703                     'fill': "none",
32704                     'pointer-events': 'none'
32705                 }
32706               ]
32707         });
32708         this.createSVG();
32709         this.svgBox = this.svgEl.dom.getScreenCTM();
32710     },
32711     createSVG : function(){ 
32712         var svg = this.signPanel;
32713         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32714         var t = this;
32715
32716         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32717         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32718         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32719         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32720         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32721         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32722         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32723         
32724     },
32725     isTouchEvent : function(e){
32726         return e.type.match(/^touch/);
32727     },
32728     getCoords : function (e) {
32729         var pt    = this.svgEl.dom.createSVGPoint();
32730         pt.x = e.clientX; 
32731         pt.y = e.clientY;
32732         if (this.isTouchEvent(e)) {
32733             pt.x =  e.targetTouches[0].clientX;
32734             pt.y = e.targetTouches[0].clientY;
32735         }
32736         var a = this.svgEl.dom.getScreenCTM();
32737         var b = a.inverse();
32738         var mx = pt.matrixTransform(b);
32739         return mx.x + ',' + mx.y;
32740     },
32741     //mouse event headler 
32742     down : function (e) {
32743         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32744         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32745         
32746         this.isMouseDown = true;
32747         
32748         e.preventDefault();
32749     },
32750     move : function (e) {
32751         if (this.isMouseDown) {
32752             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32753             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32754         }
32755         
32756         e.preventDefault();
32757     },
32758     up : function (e) {
32759         this.isMouseDown = false;
32760         var sp = this.signatureTmp.split(' ');
32761         
32762         if(sp.length > 1){
32763             if(!sp[sp.length-2].match(/^L/)){
32764                 sp.pop();
32765                 sp.pop();
32766                 sp.push("");
32767                 this.signatureTmp = sp.join(" ");
32768             }
32769         }
32770         if(this.getValue() != this.signatureTmp){
32771             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32772             this.isConfirmed = false;
32773         }
32774         e.preventDefault();
32775     },
32776     
32777     /**
32778      * Protected method that will not generally be called directly. It
32779      * is called when the editor creates its toolbar. Override this method if you need to
32780      * add custom toolbar buttons.
32781      * @param {HtmlEditor} editor
32782      */
32783     createToolbar : function(editor){
32784          function btn(id, toggle, handler){
32785             var xid = fid + '-'+ id ;
32786             return {
32787                 id : xid,
32788                 cmd : id,
32789                 cls : 'x-btn-icon x-edit-'+id,
32790                 enableToggle:toggle !== false,
32791                 scope: editor, // was editor...
32792                 handler:handler||editor.relayBtnCmd,
32793                 clickEvent:'mousedown',
32794                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32795                 tabIndex:-1
32796             };
32797         }
32798         
32799         
32800         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32801         this.tb = tb;
32802         this.tb.add(
32803            {
32804                 cls : ' x-signature-btn x-signature-'+id,
32805                 scope: editor, // was editor...
32806                 handler: this.reset,
32807                 clickEvent:'mousedown',
32808                 text: this.labels.clear
32809             },
32810             {
32811                  xtype : 'Fill',
32812                  xns: Roo.Toolbar
32813             }, 
32814             {
32815                 cls : '  x-signature-btn x-signature-'+id,
32816                 scope: editor, // was editor...
32817                 handler: this.confirmHandler,
32818                 clickEvent:'mousedown',
32819                 text: this.labels.confirm
32820             }
32821         );
32822     
32823     },
32824     //public
32825     /**
32826      * when user is clicked confirm then show this image.....
32827      * 
32828      * @return {String} Image Data URI
32829      */
32830     getImageDataURI : function(){
32831         var svg = this.svgEl.dom.parentNode.innerHTML;
32832         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32833         return src; 
32834     },
32835     /**
32836      * 
32837      * @return {Boolean} this.isConfirmed
32838      */
32839     getConfirmed : function(){
32840         return this.isConfirmed;
32841     },
32842     /**
32843      * 
32844      * @return {Number} this.width
32845      */
32846     getWidth : function(){
32847         return this.width;
32848     },
32849     /**
32850      * 
32851      * @return {Number} this.height
32852      */
32853     getHeight : function(){
32854         return this.height;
32855     },
32856     // private
32857     getSignature : function(){
32858         return this.signatureTmp;
32859     },
32860     // private
32861     reset : function(){
32862         this.signatureTmp = '';
32863         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32864         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32865         this.isConfirmed = false;
32866         Roo.form.Signature.superclass.reset.call(this);
32867     },
32868     setSignature : function(s){
32869         this.signatureTmp = s;
32870         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32871         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32872         this.setValue(s);
32873         this.isConfirmed = false;
32874         Roo.form.Signature.superclass.reset.call(this);
32875     }, 
32876     test : function(){
32877 //        Roo.log(this.signPanel.dom.contentWindow.up())
32878     },
32879     //private
32880     setConfirmed : function(){
32881         
32882         
32883         
32884 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32885     },
32886     // private
32887     confirmHandler : function(){
32888         if(!this.getSignature()){
32889             return;
32890         }
32891         
32892         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32893         this.setValue(this.getSignature());
32894         this.isConfirmed = true;
32895         
32896         this.fireEvent('confirm', this);
32897     },
32898     // private
32899     // Subclasses should provide the validation implementation by overriding this
32900     validateValue : function(value){
32901         if(this.allowBlank){
32902             return true;
32903         }
32904         
32905         if(this.isConfirmed){
32906             return true;
32907         }
32908         return false;
32909     }
32910 });/*
32911  * Based on:
32912  * Ext JS Library 1.1.1
32913  * Copyright(c) 2006-2007, Ext JS, LLC.
32914  *
32915  * Originally Released Under LGPL - original licence link has changed is not relivant.
32916  *
32917  * Fork - LGPL
32918  * <script type="text/javascript">
32919  */
32920  
32921
32922 /**
32923  * @class Roo.form.ComboBox
32924  * @extends Roo.form.TriggerField
32925  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32926  * @constructor
32927  * Create a new ComboBox.
32928  * @param {Object} config Configuration options
32929  */
32930 Roo.form.Select = function(config){
32931     Roo.form.Select.superclass.constructor.call(this, config);
32932      
32933 };
32934
32935 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32936     /**
32937      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32938      */
32939     /**
32940      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32941      * rendering into an Roo.Editor, defaults to false)
32942      */
32943     /**
32944      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32945      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32946      */
32947     /**
32948      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32949      */
32950     /**
32951      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32952      * the dropdown list (defaults to undefined, with no header element)
32953      */
32954
32955      /**
32956      * @cfg {String/Roo.Template} tpl The template to use to render the output
32957      */
32958      
32959     // private
32960     defaultAutoCreate : {tag: "select"  },
32961     /**
32962      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32963      */
32964     listWidth: undefined,
32965     /**
32966      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32967      * mode = 'remote' or 'text' if mode = 'local')
32968      */
32969     displayField: undefined,
32970     /**
32971      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32972      * mode = 'remote' or 'value' if mode = 'local'). 
32973      * Note: use of a valueField requires the user make a selection
32974      * in order for a value to be mapped.
32975      */
32976     valueField: undefined,
32977     
32978     
32979     /**
32980      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32981      * field's data value (defaults to the underlying DOM element's name)
32982      */
32983     hiddenName: undefined,
32984     /**
32985      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32986      */
32987     listClass: '',
32988     /**
32989      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32990      */
32991     selectedClass: 'x-combo-selected',
32992     /**
32993      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
32994      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32995      * which displays a downward arrow icon).
32996      */
32997     triggerClass : 'x-form-arrow-trigger',
32998     /**
32999      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33000      */
33001     shadow:'sides',
33002     /**
33003      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
33004      * anchor positions (defaults to 'tl-bl')
33005      */
33006     listAlign: 'tl-bl?',
33007     /**
33008      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
33009      */
33010     maxHeight: 300,
33011     /**
33012      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
33013      * query specified by the allQuery config option (defaults to 'query')
33014      */
33015     triggerAction: 'query',
33016     /**
33017      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
33018      * (defaults to 4, does not apply if editable = false)
33019      */
33020     minChars : 4,
33021     /**
33022      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
33023      * delay (typeAheadDelay) if it matches a known value (defaults to false)
33024      */
33025     typeAhead: false,
33026     /**
33027      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
33028      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
33029      */
33030     queryDelay: 500,
33031     /**
33032      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
33033      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
33034      */
33035     pageSize: 0,
33036     /**
33037      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
33038      * when editable = true (defaults to false)
33039      */
33040     selectOnFocus:false,
33041     /**
33042      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
33043      */
33044     queryParam: 'query',
33045     /**
33046      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
33047      * when mode = 'remote' (defaults to 'Loading...')
33048      */
33049     loadingText: 'Loading...',
33050     /**
33051      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
33052      */
33053     resizable: false,
33054     /**
33055      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
33056      */
33057     handleHeight : 8,
33058     /**
33059      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
33060      * traditional select (defaults to true)
33061      */
33062     editable: true,
33063     /**
33064      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
33065      */
33066     allQuery: '',
33067     /**
33068      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
33069      */
33070     mode: 'remote',
33071     /**
33072      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
33073      * listWidth has a higher value)
33074      */
33075     minListWidth : 70,
33076     /**
33077      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
33078      * allow the user to set arbitrary text into the field (defaults to false)
33079      */
33080     forceSelection:false,
33081     /**
33082      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
33083      * if typeAhead = true (defaults to 250)
33084      */
33085     typeAheadDelay : 250,
33086     /**
33087      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
33088      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
33089      */
33090     valueNotFoundText : undefined,
33091     
33092     /**
33093      * @cfg {String} defaultValue The value displayed after loading the store.
33094      */
33095     defaultValue: '',
33096     
33097     /**
33098      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
33099      */
33100     blockFocus : false,
33101     
33102     /**
33103      * @cfg {Boolean} disableClear Disable showing of clear button.
33104      */
33105     disableClear : false,
33106     /**
33107      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
33108      */
33109     alwaysQuery : false,
33110     
33111     //private
33112     addicon : false,
33113     editicon: false,
33114     
33115     // element that contains real text value.. (when hidden is used..)
33116      
33117     // private
33118     onRender : function(ct, position){
33119         Roo.form.Field.prototype.onRender.call(this, ct, position);
33120         
33121         if(this.store){
33122             this.store.on('beforeload', this.onBeforeLoad, this);
33123             this.store.on('load', this.onLoad, this);
33124             this.store.on('loadexception', this.onLoadException, this);
33125             this.store.load({});
33126         }
33127         
33128         
33129         
33130     },
33131
33132     // private
33133     initEvents : function(){
33134         //Roo.form.ComboBox.superclass.initEvents.call(this);
33135  
33136     },
33137
33138     onDestroy : function(){
33139        
33140         if(this.store){
33141             this.store.un('beforeload', this.onBeforeLoad, this);
33142             this.store.un('load', this.onLoad, this);
33143             this.store.un('loadexception', this.onLoadException, this);
33144         }
33145         //Roo.form.ComboBox.superclass.onDestroy.call(this);
33146     },
33147
33148     // private
33149     fireKey : function(e){
33150         if(e.isNavKeyPress() && !this.list.isVisible()){
33151             this.fireEvent("specialkey", this, e);
33152         }
33153     },
33154
33155     // private
33156     onResize: function(w, h){
33157         
33158         return; 
33159     
33160         
33161     },
33162
33163     /**
33164      * Allow or prevent the user from directly editing the field text.  If false is passed,
33165      * the user will only be able to select from the items defined in the dropdown list.  This method
33166      * is the runtime equivalent of setting the 'editable' config option at config time.
33167      * @param {Boolean} value True to allow the user to directly edit the field text
33168      */
33169     setEditable : function(value){
33170          
33171     },
33172
33173     // private
33174     onBeforeLoad : function(){
33175         
33176         Roo.log("Select before load");
33177         return;
33178     
33179         this.innerList.update(this.loadingText ?
33180                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
33181         //this.restrictHeight();
33182         this.selectedIndex = -1;
33183     },
33184
33185     // private
33186     onLoad : function(){
33187
33188     
33189         var dom = this.el.dom;
33190         dom.innerHTML = '';
33191          var od = dom.ownerDocument;
33192          
33193         if (this.emptyText) {
33194             var op = od.createElement('option');
33195             op.setAttribute('value', '');
33196             op.innerHTML = String.format('{0}', this.emptyText);
33197             dom.appendChild(op);
33198         }
33199         if(this.store.getCount() > 0){
33200            
33201             var vf = this.valueField;
33202             var df = this.displayField;
33203             this.store.data.each(function(r) {
33204                 // which colmsn to use... testing - cdoe / title..
33205                 var op = od.createElement('option');
33206                 op.setAttribute('value', r.data[vf]);
33207                 op.innerHTML = String.format('{0}', r.data[df]);
33208                 dom.appendChild(op);
33209             });
33210             if (typeof(this.defaultValue != 'undefined')) {
33211                 this.setValue(this.defaultValue);
33212             }
33213             
33214              
33215         }else{
33216             //this.onEmptyResults();
33217         }
33218         //this.el.focus();
33219     },
33220     // private
33221     onLoadException : function()
33222     {
33223         dom.innerHTML = '';
33224             
33225         Roo.log("Select on load exception");
33226         return;
33227     
33228         this.collapse();
33229         Roo.log(this.store.reader.jsonData);
33230         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
33231             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
33232         }
33233         
33234         
33235     },
33236     // private
33237     onTypeAhead : function(){
33238          
33239     },
33240
33241     // private
33242     onSelect : function(record, index){
33243         Roo.log('on select?');
33244         return;
33245         if(this.fireEvent('beforeselect', this, record, index) !== false){
33246             this.setFromData(index > -1 ? record.data : false);
33247             this.collapse();
33248             this.fireEvent('select', this, record, index);
33249         }
33250     },
33251
33252     /**
33253      * Returns the currently selected field value or empty string if no value is set.
33254      * @return {String} value The selected value
33255      */
33256     getValue : function(){
33257         var dom = this.el.dom;
33258         this.value = dom.options[dom.selectedIndex].value;
33259         return this.value;
33260         
33261     },
33262
33263     /**
33264      * Clears any text/value currently set in the field
33265      */
33266     clearValue : function(){
33267         this.value = '';
33268         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
33269         
33270     },
33271
33272     /**
33273      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
33274      * will be displayed in the field.  If the value does not match the data value of an existing item,
33275      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
33276      * Otherwise the field will be blank (although the value will still be set).
33277      * @param {String} value The value to match
33278      */
33279     setValue : function(v){
33280         var d = this.el.dom;
33281         for (var i =0; i < d.options.length;i++) {
33282             if (v == d.options[i].value) {
33283                 d.selectedIndex = i;
33284                 this.value = v;
33285                 return;
33286             }
33287         }
33288         this.clearValue();
33289     },
33290     /**
33291      * @property {Object} the last set data for the element
33292      */
33293     
33294     lastData : false,
33295     /**
33296      * Sets the value of the field based on a object which is related to the record format for the store.
33297      * @param {Object} value the value to set as. or false on reset?
33298      */
33299     setFromData : function(o){
33300         Roo.log('setfrom data?');
33301          
33302         
33303         
33304     },
33305     // private
33306     reset : function(){
33307         this.clearValue();
33308     },
33309     // private
33310     findRecord : function(prop, value){
33311         
33312         return false;
33313     
33314         var record;
33315         if(this.store.getCount() > 0){
33316             this.store.each(function(r){
33317                 if(r.data[prop] == value){
33318                     record = r;
33319                     return false;
33320                 }
33321                 return true;
33322             });
33323         }
33324         return record;
33325     },
33326     
33327     getName: function()
33328     {
33329         // returns hidden if it's set..
33330         if (!this.rendered) {return ''};
33331         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
33332         
33333     },
33334      
33335
33336     
33337
33338     // private
33339     onEmptyResults : function(){
33340         Roo.log('empty results');
33341         //this.collapse();
33342     },
33343
33344     /**
33345      * Returns true if the dropdown list is expanded, else false.
33346      */
33347     isExpanded : function(){
33348         return false;
33349     },
33350
33351     /**
33352      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
33353      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
33354      * @param {String} value The data value of the item to select
33355      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
33356      * selected item if it is not currently in view (defaults to true)
33357      * @return {Boolean} True if the value matched an item in the list, else false
33358      */
33359     selectByValue : function(v, scrollIntoView){
33360         Roo.log('select By Value');
33361         return false;
33362     
33363         if(v !== undefined && v !== null){
33364             var r = this.findRecord(this.valueField || this.displayField, v);
33365             if(r){
33366                 this.select(this.store.indexOf(r), scrollIntoView);
33367                 return true;
33368             }
33369         }
33370         return false;
33371     },
33372
33373     /**
33374      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
33375      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
33376      * @param {Number} index The zero-based index of the list item to select
33377      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
33378      * selected item if it is not currently in view (defaults to true)
33379      */
33380     select : function(index, scrollIntoView){
33381         Roo.log('select ');
33382         return  ;
33383         
33384         this.selectedIndex = index;
33385         this.view.select(index);
33386         if(scrollIntoView !== false){
33387             var el = this.view.getNode(index);
33388             if(el){
33389                 this.innerList.scrollChildIntoView(el, false);
33390             }
33391         }
33392     },
33393
33394       
33395
33396     // private
33397     validateBlur : function(){
33398         
33399         return;
33400         
33401     },
33402
33403     // private
33404     initQuery : function(){
33405         this.doQuery(this.getRawValue());
33406     },
33407
33408     // private
33409     doForce : function(){
33410         if(this.el.dom.value.length > 0){
33411             this.el.dom.value =
33412                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
33413              
33414         }
33415     },
33416
33417     /**
33418      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
33419      * query allowing the query action to be canceled if needed.
33420      * @param {String} query The SQL query to execute
33421      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
33422      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
33423      * saved in the current store (defaults to false)
33424      */
33425     doQuery : function(q, forceAll){
33426         
33427         Roo.log('doQuery?');
33428         if(q === undefined || q === null){
33429             q = '';
33430         }
33431         var qe = {
33432             query: q,
33433             forceAll: forceAll,
33434             combo: this,
33435             cancel:false
33436         };
33437         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
33438             return false;
33439         }
33440         q = qe.query;
33441         forceAll = qe.forceAll;
33442         if(forceAll === true || (q.length >= this.minChars)){
33443             if(this.lastQuery != q || this.alwaysQuery){
33444                 this.lastQuery = q;
33445                 if(this.mode == 'local'){
33446                     this.selectedIndex = -1;
33447                     if(forceAll){
33448                         this.store.clearFilter();
33449                     }else{
33450                         this.store.filter(this.displayField, q);
33451                     }
33452                     this.onLoad();
33453                 }else{
33454                     this.store.baseParams[this.queryParam] = q;
33455                     this.store.load({
33456                         params: this.getParams(q)
33457                     });
33458                     this.expand();
33459                 }
33460             }else{
33461                 this.selectedIndex = -1;
33462                 this.onLoad();   
33463             }
33464         }
33465     },
33466
33467     // private
33468     getParams : function(q){
33469         var p = {};
33470         //p[this.queryParam] = q;
33471         if(this.pageSize){
33472             p.start = 0;
33473             p.limit = this.pageSize;
33474         }
33475         return p;
33476     },
33477
33478     /**
33479      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
33480      */
33481     collapse : function(){
33482         
33483     },
33484
33485     // private
33486     collapseIf : function(e){
33487         
33488     },
33489
33490     /**
33491      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
33492      */
33493     expand : function(){
33494         
33495     } ,
33496
33497     // private
33498      
33499
33500     /** 
33501     * @cfg {Boolean} grow 
33502     * @hide 
33503     */
33504     /** 
33505     * @cfg {Number} growMin 
33506     * @hide 
33507     */
33508     /** 
33509     * @cfg {Number} growMax 
33510     * @hide 
33511     */
33512     /**
33513      * @hide
33514      * @method autoSize
33515      */
33516     
33517     setWidth : function()
33518     {
33519         
33520     },
33521     getResizeEl : function(){
33522         return this.el;
33523     }
33524 });//<script type="text/javasscript">
33525  
33526
33527 /**
33528  * @class Roo.DDView
33529  * A DnD enabled version of Roo.View.
33530  * @param {Element/String} container The Element in which to create the View.
33531  * @param {String} tpl The template string used to create the markup for each element of the View
33532  * @param {Object} config The configuration properties. These include all the config options of
33533  * {@link Roo.View} plus some specific to this class.<br>
33534  * <p>
33535  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
33536  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
33537  * <p>
33538  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
33539 .x-view-drag-insert-above {
33540         border-top:1px dotted #3366cc;
33541 }
33542 .x-view-drag-insert-below {
33543         border-bottom:1px dotted #3366cc;
33544 }
33545 </code></pre>
33546  * 
33547  */
33548  
33549 Roo.DDView = function(container, tpl, config) {
33550     Roo.DDView.superclass.constructor.apply(this, arguments);
33551     this.getEl().setStyle("outline", "0px none");
33552     this.getEl().unselectable();
33553     if (this.dragGroup) {
33554         this.setDraggable(this.dragGroup.split(","));
33555     }
33556     if (this.dropGroup) {
33557         this.setDroppable(this.dropGroup.split(","));
33558     }
33559     if (this.deletable) {
33560         this.setDeletable();
33561     }
33562     this.isDirtyFlag = false;
33563         this.addEvents({
33564                 "drop" : true
33565         });
33566 };
33567
33568 Roo.extend(Roo.DDView, Roo.View, {
33569 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
33570 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
33571 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
33572 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
33573
33574         isFormField: true,
33575
33576         reset: Roo.emptyFn,
33577         
33578         clearInvalid: Roo.form.Field.prototype.clearInvalid,
33579
33580         validate: function() {
33581                 return true;
33582         },
33583         
33584         destroy: function() {
33585                 this.purgeListeners();
33586                 this.getEl.removeAllListeners();
33587                 this.getEl().remove();
33588                 if (this.dragZone) {
33589                         if (this.dragZone.destroy) {
33590                                 this.dragZone.destroy();
33591                         }
33592                 }
33593                 if (this.dropZone) {
33594                         if (this.dropZone.destroy) {
33595                                 this.dropZone.destroy();
33596                         }
33597                 }
33598         },
33599
33600 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
33601         getName: function() {
33602                 return this.name;
33603         },
33604
33605 /**     Loads the View from a JSON string representing the Records to put into the Store. */
33606         setValue: function(v) {
33607                 if (!this.store) {
33608                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
33609                 }
33610                 var data = {};
33611                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
33612                 this.store.proxy = new Roo.data.MemoryProxy(data);
33613                 this.store.load();
33614         },
33615
33616 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
33617         getValue: function() {
33618                 var result = '(';
33619                 this.store.each(function(rec) {
33620                         result += rec.id + ',';
33621                 });
33622                 return result.substr(0, result.length - 1) + ')';
33623         },
33624         
33625         getIds: function() {
33626                 var i = 0, result = new Array(this.store.getCount());
33627                 this.store.each(function(rec) {
33628                         result[i++] = rec.id;
33629                 });
33630                 return result;
33631         },
33632         
33633         isDirty: function() {
33634                 return this.isDirtyFlag;
33635         },
33636
33637 /**
33638  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
33639  *      whole Element becomes the target, and this causes the drop gesture to append.
33640  */
33641     getTargetFromEvent : function(e) {
33642                 var target = e.getTarget();
33643                 while ((target !== null) && (target.parentNode != this.el.dom)) {
33644                 target = target.parentNode;
33645                 }
33646                 if (!target) {
33647                         target = this.el.dom.lastChild || this.el.dom;
33648                 }
33649                 return target;
33650     },
33651
33652 /**
33653  *      Create the drag data which consists of an object which has the property "ddel" as
33654  *      the drag proxy element. 
33655  */
33656     getDragData : function(e) {
33657         var target = this.findItemFromChild(e.getTarget());
33658                 if(target) {
33659                         this.handleSelection(e);
33660                         var selNodes = this.getSelectedNodes();
33661             var dragData = {
33662                 source: this,
33663                 copy: this.copy || (this.allowCopy && e.ctrlKey),
33664                 nodes: selNodes,
33665                 records: []
33666                         };
33667                         var selectedIndices = this.getSelectedIndexes();
33668                         for (var i = 0; i < selectedIndices.length; i++) {
33669                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
33670                         }
33671                         if (selNodes.length == 1) {
33672                                 dragData.ddel = target.cloneNode(true); // the div element
33673                         } else {
33674                                 var div = document.createElement('div'); // create the multi element drag "ghost"
33675                                 div.className = 'multi-proxy';
33676                                 for (var i = 0, len = selNodes.length; i < len; i++) {
33677                                         div.appendChild(selNodes[i].cloneNode(true));
33678                                 }
33679                                 dragData.ddel = div;
33680                         }
33681             //console.log(dragData)
33682             //console.log(dragData.ddel.innerHTML)
33683                         return dragData;
33684                 }
33685         //console.log('nodragData')
33686                 return false;
33687     },
33688     
33689 /**     Specify to which ddGroup items in this DDView may be dragged. */
33690     setDraggable: function(ddGroup) {
33691         if (ddGroup instanceof Array) {
33692                 Roo.each(ddGroup, this.setDraggable, this);
33693                 return;
33694         }
33695         if (this.dragZone) {
33696                 this.dragZone.addToGroup(ddGroup);
33697         } else {
33698                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33699                                 containerScroll: true,
33700                                 ddGroup: ddGroup 
33701
33702                         });
33703 //                      Draggability implies selection. DragZone's mousedown selects the element.
33704                         if (!this.multiSelect) { this.singleSelect = true; }
33705
33706 //                      Wire the DragZone's handlers up to methods in *this*
33707                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
33708                 }
33709     },
33710
33711 /**     Specify from which ddGroup this DDView accepts drops. */
33712     setDroppable: function(ddGroup) {
33713         if (ddGroup instanceof Array) {
33714                 Roo.each(ddGroup, this.setDroppable, this);
33715                 return;
33716         }
33717         if (this.dropZone) {
33718                 this.dropZone.addToGroup(ddGroup);
33719         } else {
33720                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33721                                 containerScroll: true,
33722                                 ddGroup: ddGroup
33723                         });
33724
33725 //                      Wire the DropZone's handlers up to methods in *this*
33726                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33727                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33728                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33729                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33730                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33731                 }
33732     },
33733
33734 /**     Decide whether to drop above or below a View node. */
33735     getDropPoint : function(e, n, dd){
33736         if (n == this.el.dom) { return "above"; }
33737                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33738                 var c = t + (b - t) / 2;
33739                 var y = Roo.lib.Event.getPageY(e);
33740                 if(y <= c) {
33741                         return "above";
33742                 }else{
33743                         return "below";
33744                 }
33745     },
33746
33747     onNodeEnter : function(n, dd, e, data){
33748                 return false;
33749     },
33750     
33751     onNodeOver : function(n, dd, e, data){
33752                 var pt = this.getDropPoint(e, n, dd);
33753                 // set the insert point style on the target node
33754                 var dragElClass = this.dropNotAllowed;
33755                 if (pt) {
33756                         var targetElClass;
33757                         if (pt == "above"){
33758                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33759                                 targetElClass = "x-view-drag-insert-above";
33760                         } else {
33761                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33762                                 targetElClass = "x-view-drag-insert-below";
33763                         }
33764                         if (this.lastInsertClass != targetElClass){
33765                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33766                                 this.lastInsertClass = targetElClass;
33767                         }
33768                 }
33769                 return dragElClass;
33770         },
33771
33772     onNodeOut : function(n, dd, e, data){
33773                 this.removeDropIndicators(n);
33774     },
33775
33776     onNodeDrop : function(n, dd, e, data){
33777         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33778                 return false;
33779         }
33780         var pt = this.getDropPoint(e, n, dd);
33781                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33782                 if (pt == "below") { insertAt++; }
33783                 for (var i = 0; i < data.records.length; i++) {
33784                         var r = data.records[i];
33785                         var dup = this.store.getById(r.id);
33786                         if (dup && (dd != this.dragZone)) {
33787                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33788                         } else {
33789                                 if (data.copy) {
33790                                         this.store.insert(insertAt++, r.copy());
33791                                 } else {
33792                                         data.source.isDirtyFlag = true;
33793                                         r.store.remove(r);
33794                                         this.store.insert(insertAt++, r);
33795                                 }
33796                                 this.isDirtyFlag = true;
33797                         }
33798                 }
33799                 this.dragZone.cachedTarget = null;
33800                 return true;
33801     },
33802
33803     removeDropIndicators : function(n){
33804                 if(n){
33805                         Roo.fly(n).removeClass([
33806                                 "x-view-drag-insert-above",
33807                                 "x-view-drag-insert-below"]);
33808                         this.lastInsertClass = "_noclass";
33809                 }
33810     },
33811
33812 /**
33813  *      Utility method. Add a delete option to the DDView's context menu.
33814  *      @param {String} imageUrl The URL of the "delete" icon image.
33815  */
33816         setDeletable: function(imageUrl) {
33817                 if (!this.singleSelect && !this.multiSelect) {
33818                         this.singleSelect = true;
33819                 }
33820                 var c = this.getContextMenu();
33821                 this.contextMenu.on("itemclick", function(item) {
33822                         switch (item.id) {
33823                                 case "delete":
33824                                         this.remove(this.getSelectedIndexes());
33825                                         break;
33826                         }
33827                 }, this);
33828                 this.contextMenu.add({
33829                         icon: imageUrl,
33830                         id: "delete",
33831                         text: 'Delete'
33832                 });
33833         },
33834         
33835 /**     Return the context menu for this DDView. */
33836         getContextMenu: function() {
33837                 if (!this.contextMenu) {
33838 //                      Create the View's context menu
33839                         this.contextMenu = new Roo.menu.Menu({
33840                                 id: this.id + "-contextmenu"
33841                         });
33842                         this.el.on("contextmenu", this.showContextMenu, this);
33843                 }
33844                 return this.contextMenu;
33845         },
33846         
33847         disableContextMenu: function() {
33848                 if (this.contextMenu) {
33849                         this.el.un("contextmenu", this.showContextMenu, this);
33850                 }
33851         },
33852
33853         showContextMenu: function(e, item) {
33854         item = this.findItemFromChild(e.getTarget());
33855                 if (item) {
33856                         e.stopEvent();
33857                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33858                         this.contextMenu.showAt(e.getXY());
33859             }
33860     },
33861
33862 /**
33863  *      Remove {@link Roo.data.Record}s at the specified indices.
33864  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33865  */
33866     remove: function(selectedIndices) {
33867                 selectedIndices = [].concat(selectedIndices);
33868                 for (var i = 0; i < selectedIndices.length; i++) {
33869                         var rec = this.store.getAt(selectedIndices[i]);
33870                         this.store.remove(rec);
33871                 }
33872     },
33873
33874 /**
33875  *      Double click fires the event, but also, if this is draggable, and there is only one other
33876  *      related DropZone, it transfers the selected node.
33877  */
33878     onDblClick : function(e){
33879         var item = this.findItemFromChild(e.getTarget());
33880         if(item){
33881             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33882                 return false;
33883             }
33884             if (this.dragGroup) {
33885                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33886                     while (targets.indexOf(this.dropZone) > -1) {
33887                             targets.remove(this.dropZone);
33888                                 }
33889                     if (targets.length == 1) {
33890                                         this.dragZone.cachedTarget = null;
33891                         var el = Roo.get(targets[0].getEl());
33892                         var box = el.getBox(true);
33893                         targets[0].onNodeDrop(el.dom, {
33894                                 target: el.dom,
33895                                 xy: [box.x, box.y + box.height - 1]
33896                         }, null, this.getDragData(e));
33897                     }
33898                 }
33899         }
33900     },
33901     
33902     handleSelection: function(e) {
33903                 this.dragZone.cachedTarget = null;
33904         var item = this.findItemFromChild(e.getTarget());
33905         if (!item) {
33906                 this.clearSelections(true);
33907                 return;
33908         }
33909                 if (item && (this.multiSelect || this.singleSelect)){
33910                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33911                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33912                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33913                                 this.unselect(item);
33914                         } else {
33915                                 this.select(item, this.multiSelect && e.ctrlKey);
33916                                 this.lastSelection = item;
33917                         }
33918                 }
33919     },
33920
33921     onItemClick : function(item, index, e){
33922                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33923                         return false;
33924                 }
33925                 return true;
33926     },
33927
33928     unselect : function(nodeInfo, suppressEvent){
33929                 var node = this.getNode(nodeInfo);
33930                 if(node && this.isSelected(node)){
33931                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33932                                 Roo.fly(node).removeClass(this.selectedClass);
33933                                 this.selections.remove(node);
33934                                 if(!suppressEvent){
33935                                         this.fireEvent("selectionchange", this, this.selections);
33936                                 }
33937                         }
33938                 }
33939     }
33940 });
33941 /*
33942  * Based on:
33943  * Ext JS Library 1.1.1
33944  * Copyright(c) 2006-2007, Ext JS, LLC.
33945  *
33946  * Originally Released Under LGPL - original licence link has changed is not relivant.
33947  *
33948  * Fork - LGPL
33949  * <script type="text/javascript">
33950  */
33951  
33952 /**
33953  * @class Roo.LayoutManager
33954  * @extends Roo.util.Observable
33955  * Base class for layout managers.
33956  */
33957 Roo.LayoutManager = function(container, config){
33958     Roo.LayoutManager.superclass.constructor.call(this);
33959     this.el = Roo.get(container);
33960     // ie scrollbar fix
33961     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33962         document.body.scroll = "no";
33963     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33964         this.el.position('relative');
33965     }
33966     this.id = this.el.id;
33967     this.el.addClass("x-layout-container");
33968     /** false to disable window resize monitoring @type Boolean */
33969     this.monitorWindowResize = true;
33970     this.regions = {};
33971     this.addEvents({
33972         /**
33973          * @event layout
33974          * Fires when a layout is performed. 
33975          * @param {Roo.LayoutManager} this
33976          */
33977         "layout" : true,
33978         /**
33979          * @event regionresized
33980          * Fires when the user resizes a region. 
33981          * @param {Roo.LayoutRegion} region The resized region
33982          * @param {Number} newSize The new size (width for east/west, height for north/south)
33983          */
33984         "regionresized" : true,
33985         /**
33986          * @event regioncollapsed
33987          * Fires when a region is collapsed. 
33988          * @param {Roo.LayoutRegion} region The collapsed region
33989          */
33990         "regioncollapsed" : true,
33991         /**
33992          * @event regionexpanded
33993          * Fires when a region is expanded.  
33994          * @param {Roo.LayoutRegion} region The expanded region
33995          */
33996         "regionexpanded" : true
33997     });
33998     this.updating = false;
33999     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34000 };
34001
34002 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
34003     /**
34004      * Returns true if this layout is currently being updated
34005      * @return {Boolean}
34006      */
34007     isUpdating : function(){
34008         return this.updating; 
34009     },
34010     
34011     /**
34012      * Suspend the LayoutManager from doing auto-layouts while
34013      * making multiple add or remove calls
34014      */
34015     beginUpdate : function(){
34016         this.updating = true;    
34017     },
34018     
34019     /**
34020      * Restore auto-layouts and optionally disable the manager from performing a layout
34021      * @param {Boolean} noLayout true to disable a layout update 
34022      */
34023     endUpdate : function(noLayout){
34024         this.updating = false;
34025         if(!noLayout){
34026             this.layout();
34027         }    
34028     },
34029     
34030     layout: function(){
34031         
34032     },
34033     
34034     onRegionResized : function(region, newSize){
34035         this.fireEvent("regionresized", region, newSize);
34036         this.layout();
34037     },
34038     
34039     onRegionCollapsed : function(region){
34040         this.fireEvent("regioncollapsed", region);
34041     },
34042     
34043     onRegionExpanded : function(region){
34044         this.fireEvent("regionexpanded", region);
34045     },
34046         
34047     /**
34048      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34049      * performs box-model adjustments.
34050      * @return {Object} The size as an object {width: (the width), height: (the height)}
34051      */
34052     getViewSize : function(){
34053         var size;
34054         if(this.el.dom != document.body){
34055             size = this.el.getSize();
34056         }else{
34057             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34058         }
34059         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34060         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34061         return size;
34062     },
34063     
34064     /**
34065      * Returns the Element this layout is bound to.
34066      * @return {Roo.Element}
34067      */
34068     getEl : function(){
34069         return this.el;
34070     },
34071     
34072     /**
34073      * Returns the specified region.
34074      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34075      * @return {Roo.LayoutRegion}
34076      */
34077     getRegion : function(target){
34078         return this.regions[target.toLowerCase()];
34079     },
34080     
34081     onWindowResize : function(){
34082         if(this.monitorWindowResize){
34083             this.layout();
34084         }
34085     }
34086 });/*
34087  * Based on:
34088  * Ext JS Library 1.1.1
34089  * Copyright(c) 2006-2007, Ext JS, LLC.
34090  *
34091  * Originally Released Under LGPL - original licence link has changed is not relivant.
34092  *
34093  * Fork - LGPL
34094  * <script type="text/javascript">
34095  */
34096 /**
34097  * @class Roo.BorderLayout
34098  * @extends Roo.LayoutManager
34099  * @children Roo.ContentPanel
34100  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34101  * please see: <br><br>
34102  * <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>
34103  * <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>
34104  * Example:
34105  <pre><code>
34106  var layout = new Roo.BorderLayout(document.body, {
34107     north: {
34108         initialSize: 25,
34109         titlebar: false
34110     },
34111     west: {
34112         split:true,
34113         initialSize: 200,
34114         minSize: 175,
34115         maxSize: 400,
34116         titlebar: true,
34117         collapsible: true
34118     },
34119     east: {
34120         split:true,
34121         initialSize: 202,
34122         minSize: 175,
34123         maxSize: 400,
34124         titlebar: true,
34125         collapsible: true
34126     },
34127     south: {
34128         split:true,
34129         initialSize: 100,
34130         minSize: 100,
34131         maxSize: 200,
34132         titlebar: true,
34133         collapsible: true
34134     },
34135     center: {
34136         titlebar: true,
34137         autoScroll:true,
34138         resizeTabs: true,
34139         minTabWidth: 50,
34140         preferredTabWidth: 150
34141     }
34142 });
34143
34144 // shorthand
34145 var CP = Roo.ContentPanel;
34146
34147 layout.beginUpdate();
34148 layout.add("north", new CP("north", "North"));
34149 layout.add("south", new CP("south", {title: "South", closable: true}));
34150 layout.add("west", new CP("west", {title: "West"}));
34151 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
34152 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
34153 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
34154 layout.getRegion("center").showPanel("center1");
34155 layout.endUpdate();
34156 </code></pre>
34157
34158 <b>The container the layout is rendered into can be either the body element or any other element.
34159 If it is not the body element, the container needs to either be an absolute positioned element,
34160 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34161 the container size if it is not the body element.</b>
34162
34163 * @constructor
34164 * Create a new BorderLayout
34165 * @param {String/HTMLElement/Element} container The container this layout is bound to
34166 * @param {Object} config Configuration options
34167  */
34168 Roo.BorderLayout = function(container, config){
34169     config = config || {};
34170     Roo.BorderLayout.superclass.constructor.call(this, container, config);
34171     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
34172     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
34173         var target = this.factory.validRegions[i];
34174         if(config[target]){
34175             this.addRegion(target, config[target]);
34176         }
34177     }
34178 };
34179
34180 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
34181         
34182         /**
34183          * @cfg {Roo.LayoutRegion} east
34184          */
34185         /**
34186          * @cfg {Roo.LayoutRegion} west
34187          */
34188         /**
34189          * @cfg {Roo.LayoutRegion} north
34190          */
34191         /**
34192          * @cfg {Roo.LayoutRegion} south
34193          */
34194         /**
34195          * @cfg {Roo.LayoutRegion} center
34196          */
34197     /**
34198      * Creates and adds a new region if it doesn't already exist.
34199      * @param {String} target The target region key (north, south, east, west or center).
34200      * @param {Object} config The regions config object
34201      * @return {BorderLayoutRegion} The new region
34202      */
34203     addRegion : function(target, config){
34204         if(!this.regions[target]){
34205             var r = this.factory.create(target, this, config);
34206             this.bindRegion(target, r);
34207         }
34208         return this.regions[target];
34209     },
34210
34211     // private (kinda)
34212     bindRegion : function(name, r){
34213         this.regions[name] = r;
34214         r.on("visibilitychange", this.layout, this);
34215         r.on("paneladded", this.layout, this);
34216         r.on("panelremoved", this.layout, this);
34217         r.on("invalidated", this.layout, this);
34218         r.on("resized", this.onRegionResized, this);
34219         r.on("collapsed", this.onRegionCollapsed, this);
34220         r.on("expanded", this.onRegionExpanded, this);
34221     },
34222
34223     /**
34224      * Performs a layout update.
34225      */
34226     layout : function(){
34227         if(this.updating) {
34228             return;
34229         }
34230         var size = this.getViewSize();
34231         var w = size.width;
34232         var h = size.height;
34233         var centerW = w;
34234         var centerH = h;
34235         var centerY = 0;
34236         var centerX = 0;
34237         //var x = 0, y = 0;
34238
34239         var rs = this.regions;
34240         var north = rs["north"];
34241         var south = rs["south"]; 
34242         var west = rs["west"];
34243         var east = rs["east"];
34244         var center = rs["center"];
34245         //if(this.hideOnLayout){ // not supported anymore
34246             //c.el.setStyle("display", "none");
34247         //}
34248         if(north && north.isVisible()){
34249             var b = north.getBox();
34250             var m = north.getMargins();
34251             b.width = w - (m.left+m.right);
34252             b.x = m.left;
34253             b.y = m.top;
34254             centerY = b.height + b.y + m.bottom;
34255             centerH -= centerY;
34256             north.updateBox(this.safeBox(b));
34257         }
34258         if(south && south.isVisible()){
34259             var b = south.getBox();
34260             var m = south.getMargins();
34261             b.width = w - (m.left+m.right);
34262             b.x = m.left;
34263             var totalHeight = (b.height + m.top + m.bottom);
34264             b.y = h - totalHeight + m.top;
34265             centerH -= totalHeight;
34266             south.updateBox(this.safeBox(b));
34267         }
34268         if(west && west.isVisible()){
34269             var b = west.getBox();
34270             var m = west.getMargins();
34271             b.height = centerH - (m.top+m.bottom);
34272             b.x = m.left;
34273             b.y = centerY + m.top;
34274             var totalWidth = (b.width + m.left + m.right);
34275             centerX += totalWidth;
34276             centerW -= totalWidth;
34277             west.updateBox(this.safeBox(b));
34278         }
34279         if(east && east.isVisible()){
34280             var b = east.getBox();
34281             var m = east.getMargins();
34282             b.height = centerH - (m.top+m.bottom);
34283             var totalWidth = (b.width + m.left + m.right);
34284             b.x = w - totalWidth + m.left;
34285             b.y = centerY + m.top;
34286             centerW -= totalWidth;
34287             east.updateBox(this.safeBox(b));
34288         }
34289         if(center){
34290             var m = center.getMargins();
34291             var centerBox = {
34292                 x: centerX + m.left,
34293                 y: centerY + m.top,
34294                 width: centerW - (m.left+m.right),
34295                 height: centerH - (m.top+m.bottom)
34296             };
34297             //if(this.hideOnLayout){
34298                 //center.el.setStyle("display", "block");
34299             //}
34300             center.updateBox(this.safeBox(centerBox));
34301         }
34302         this.el.repaint();
34303         this.fireEvent("layout", this);
34304     },
34305
34306     // private
34307     safeBox : function(box){
34308         box.width = Math.max(0, box.width);
34309         box.height = Math.max(0, box.height);
34310         return box;
34311     },
34312
34313     /**
34314      * Adds a ContentPanel (or subclass) to this layout.
34315      * @param {String} target The target region key (north, south, east, west or center).
34316      * @param {Roo.ContentPanel} panel The panel to add
34317      * @return {Roo.ContentPanel} The added panel
34318      */
34319     add : function(target, panel){
34320          
34321         target = target.toLowerCase();
34322         return this.regions[target].add(panel);
34323     },
34324
34325     /**
34326      * Remove a ContentPanel (or subclass) to this layout.
34327      * @param {String} target The target region key (north, south, east, west or center).
34328      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34329      * @return {Roo.ContentPanel} The removed panel
34330      */
34331     remove : function(target, panel){
34332         target = target.toLowerCase();
34333         return this.regions[target].remove(panel);
34334     },
34335
34336     /**
34337      * Searches all regions for a panel with the specified id
34338      * @param {String} panelId
34339      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34340      */
34341     findPanel : function(panelId){
34342         var rs = this.regions;
34343         for(var target in rs){
34344             if(typeof rs[target] != "function"){
34345                 var p = rs[target].getPanel(panelId);
34346                 if(p){
34347                     return p;
34348                 }
34349             }
34350         }
34351         return null;
34352     },
34353
34354     /**
34355      * Searches all regions for a panel with the specified id and activates (shows) it.
34356      * @param {String/ContentPanel} panelId The panels id or the panel itself
34357      * @return {Roo.ContentPanel} The shown panel or null
34358      */
34359     showPanel : function(panelId) {
34360       var rs = this.regions;
34361       for(var target in rs){
34362          var r = rs[target];
34363          if(typeof r != "function"){
34364             if(r.hasPanel(panelId)){
34365                return r.showPanel(panelId);
34366             }
34367          }
34368       }
34369       return null;
34370    },
34371
34372    /**
34373      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34374      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34375      */
34376     restoreState : function(provider){
34377         if(!provider){
34378             provider = Roo.state.Manager;
34379         }
34380         var sm = new Roo.LayoutStateManager();
34381         sm.init(this, provider);
34382     },
34383
34384     /**
34385      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
34386      * object should contain properties for each region to add ContentPanels to, and each property's value should be
34387      * a valid ContentPanel config object.  Example:
34388      * <pre><code>
34389 // Create the main layout
34390 var layout = new Roo.BorderLayout('main-ct', {
34391     west: {
34392         split:true,
34393         minSize: 175,
34394         titlebar: true
34395     },
34396     center: {
34397         title:'Components'
34398     }
34399 }, 'main-ct');
34400
34401 // Create and add multiple ContentPanels at once via configs
34402 layout.batchAdd({
34403    west: {
34404        id: 'source-files',
34405        autoCreate:true,
34406        title:'Ext Source Files',
34407        autoScroll:true,
34408        fitToFrame:true
34409    },
34410    center : {
34411        el: cview,
34412        autoScroll:true,
34413        fitToFrame:true,
34414        toolbar: tb,
34415        resizeEl:'cbody'
34416    }
34417 });
34418 </code></pre>
34419      * @param {Object} regions An object containing ContentPanel configs by region name
34420      */
34421     batchAdd : function(regions){
34422         this.beginUpdate();
34423         for(var rname in regions){
34424             var lr = this.regions[rname];
34425             if(lr){
34426                 this.addTypedPanels(lr, regions[rname]);
34427             }
34428         }
34429         this.endUpdate();
34430     },
34431
34432     // private
34433     addTypedPanels : function(lr, ps){
34434         if(typeof ps == 'string'){
34435             lr.add(new Roo.ContentPanel(ps));
34436         }
34437         else if(ps instanceof Array){
34438             for(var i =0, len = ps.length; i < len; i++){
34439                 this.addTypedPanels(lr, ps[i]);
34440             }
34441         }
34442         else if(!ps.events){ // raw config?
34443             var el = ps.el;
34444             delete ps.el; // prevent conflict
34445             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
34446         }
34447         else {  // panel object assumed!
34448             lr.add(ps);
34449         }
34450     },
34451     /**
34452      * Adds a xtype elements to the layout.
34453      * <pre><code>
34454
34455 layout.addxtype({
34456        xtype : 'ContentPanel',
34457        region: 'west',
34458        items: [ .... ]
34459    }
34460 );
34461
34462 layout.addxtype({
34463         xtype : 'NestedLayoutPanel',
34464         region: 'west',
34465         layout: {
34466            center: { },
34467            west: { }   
34468         },
34469         items : [ ... list of content panels or nested layout panels.. ]
34470    }
34471 );
34472 </code></pre>
34473      * @param {Object} cfg Xtype definition of item to add.
34474      */
34475     addxtype : function(cfg)
34476     {
34477         // basically accepts a pannel...
34478         // can accept a layout region..!?!?
34479         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34480         
34481         if (!cfg.xtype.match(/Panel$/)) {
34482             return false;
34483         }
34484         var ret = false;
34485         
34486         if (typeof(cfg.region) == 'undefined') {
34487             Roo.log("Failed to add Panel, region was not set");
34488             Roo.log(cfg);
34489             return false;
34490         }
34491         var region = cfg.region;
34492         delete cfg.region;
34493         
34494           
34495         var xitems = [];
34496         if (cfg.items) {
34497             xitems = cfg.items;
34498             delete cfg.items;
34499         }
34500         var nb = false;
34501         
34502         switch(cfg.xtype) 
34503         {
34504             case 'ContentPanel':  // ContentPanel (el, cfg)
34505             case 'ScrollPanel':  // ContentPanel (el, cfg)
34506             case 'ViewPanel': 
34507                 if(cfg.autoCreate) {
34508                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34509                 } else {
34510                     var el = this.el.createChild();
34511                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34512                 }
34513                 
34514                 this.add(region, ret);
34515                 break;
34516             
34517             
34518             case 'TreePanel': // our new panel!
34519                 cfg.el = this.el.createChild();
34520                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34521                 this.add(region, ret);
34522                 break;
34523             
34524             case 'NestedLayoutPanel': 
34525                 // create a new Layout (which is  a Border Layout...
34526                 var el = this.el.createChild();
34527                 var clayout = cfg.layout;
34528                 delete cfg.layout;
34529                 clayout.items   = clayout.items  || [];
34530                 // replace this exitems with the clayout ones..
34531                 xitems = clayout.items;
34532                  
34533                 
34534                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34535                     cfg.background = false;
34536                 }
34537                 var layout = new Roo.BorderLayout(el, clayout);
34538                 
34539                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
34540                 //console.log('adding nested layout panel '  + cfg.toSource());
34541                 this.add(region, ret);
34542                 nb = {}; /// find first...
34543                 break;
34544                 
34545             case 'GridPanel': 
34546             
34547                 // needs grid and region
34548                 
34549                 //var el = this.getRegion(region).el.createChild();
34550                 var el = this.el.createChild();
34551                 // create the grid first...
34552                 
34553                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
34554                 delete cfg.grid;
34555                 if (region == 'center' && this.active ) {
34556                     cfg.background = false;
34557                 }
34558                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
34559                 
34560                 this.add(region, ret);
34561                 if (cfg.background) {
34562                     ret.on('activate', function(gp) {
34563                         if (!gp.grid.rendered) {
34564                             gp.grid.render();
34565                         }
34566                     });
34567                 } else {
34568                     grid.render();
34569                 }
34570                 break;
34571            
34572            
34573            
34574                 
34575                 
34576                 
34577             default:
34578                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34579                     
34580                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34581                     this.add(region, ret);
34582                 } else {
34583                 
34584                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
34585                     return null;
34586                 }
34587                 
34588              // GridPanel (grid, cfg)
34589             
34590         }
34591         this.beginUpdate();
34592         // add children..
34593         var region = '';
34594         var abn = {};
34595         Roo.each(xitems, function(i)  {
34596             region = nb && i.region ? i.region : false;
34597             
34598             var add = ret.addxtype(i);
34599            
34600             if (region) {
34601                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34602                 if (!i.background) {
34603                     abn[region] = nb[region] ;
34604                 }
34605             }
34606             
34607         });
34608         this.endUpdate();
34609
34610         // make the last non-background panel active..
34611         //if (nb) { Roo.log(abn); }
34612         if (nb) {
34613             
34614             for(var r in abn) {
34615                 region = this.getRegion(r);
34616                 if (region) {
34617                     // tried using nb[r], but it does not work..
34618                      
34619                     region.showPanel(abn[r]);
34620                    
34621                 }
34622             }
34623         }
34624         return ret;
34625         
34626     }
34627 });
34628
34629 /**
34630  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
34631  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
34632  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
34633  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
34634  * <pre><code>
34635 // shorthand
34636 var CP = Roo.ContentPanel;
34637
34638 var layout = Roo.BorderLayout.create({
34639     north: {
34640         initialSize: 25,
34641         titlebar: false,
34642         panels: [new CP("north", "North")]
34643     },
34644     west: {
34645         split:true,
34646         initialSize: 200,
34647         minSize: 175,
34648         maxSize: 400,
34649         titlebar: true,
34650         collapsible: true,
34651         panels: [new CP("west", {title: "West"})]
34652     },
34653     east: {
34654         split:true,
34655         initialSize: 202,
34656         minSize: 175,
34657         maxSize: 400,
34658         titlebar: true,
34659         collapsible: true,
34660         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34661     },
34662     south: {
34663         split:true,
34664         initialSize: 100,
34665         minSize: 100,
34666         maxSize: 200,
34667         titlebar: true,
34668         collapsible: true,
34669         panels: [new CP("south", {title: "South", closable: true})]
34670     },
34671     center: {
34672         titlebar: true,
34673         autoScroll:true,
34674         resizeTabs: true,
34675         minTabWidth: 50,
34676         preferredTabWidth: 150,
34677         panels: [
34678             new CP("center1", {title: "Close Me", closable: true}),
34679             new CP("center2", {title: "Center Panel", closable: false})
34680         ]
34681     }
34682 }, document.body);
34683
34684 layout.getRegion("center").showPanel("center1");
34685 </code></pre>
34686  * @param config
34687  * @param targetEl
34688  */
34689 Roo.BorderLayout.create = function(config, targetEl){
34690     var layout = new Roo.BorderLayout(targetEl || document.body, config);
34691     layout.beginUpdate();
34692     var regions = Roo.BorderLayout.RegionFactory.validRegions;
34693     for(var j = 0, jlen = regions.length; j < jlen; j++){
34694         var lr = regions[j];
34695         if(layout.regions[lr] && config[lr].panels){
34696             var r = layout.regions[lr];
34697             var ps = config[lr].panels;
34698             layout.addTypedPanels(r, ps);
34699         }
34700     }
34701     layout.endUpdate();
34702     return layout;
34703 };
34704
34705 // private
34706 Roo.BorderLayout.RegionFactory = {
34707     // private
34708     validRegions : ["north","south","east","west","center"],
34709
34710     // private
34711     create : function(target, mgr, config){
34712         target = target.toLowerCase();
34713         if(config.lightweight || config.basic){
34714             return new Roo.BasicLayoutRegion(mgr, config, target);
34715         }
34716         switch(target){
34717             case "north":
34718                 return new Roo.NorthLayoutRegion(mgr, config);
34719             case "south":
34720                 return new Roo.SouthLayoutRegion(mgr, config);
34721             case "east":
34722                 return new Roo.EastLayoutRegion(mgr, config);
34723             case "west":
34724                 return new Roo.WestLayoutRegion(mgr, config);
34725             case "center":
34726                 return new Roo.CenterLayoutRegion(mgr, config);
34727         }
34728         throw 'Layout region "'+target+'" not supported.';
34729     }
34730 };/*
34731  * Based on:
34732  * Ext JS Library 1.1.1
34733  * Copyright(c) 2006-2007, Ext JS, LLC.
34734  *
34735  * Originally Released Under LGPL - original licence link has changed is not relivant.
34736  *
34737  * Fork - LGPL
34738  * <script type="text/javascript">
34739  */
34740  
34741 /**
34742  * @class Roo.BasicLayoutRegion
34743  * @extends Roo.util.Observable
34744  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34745  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34746  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34747  */
34748 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34749     this.mgr = mgr;
34750     this.position  = pos;
34751     this.events = {
34752         /**
34753          * @scope Roo.BasicLayoutRegion
34754          */
34755         
34756         /**
34757          * @event beforeremove
34758          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34759          * @param {Roo.LayoutRegion} this
34760          * @param {Roo.ContentPanel} panel The panel
34761          * @param {Object} e The cancel event object
34762          */
34763         "beforeremove" : true,
34764         /**
34765          * @event invalidated
34766          * Fires when the layout for this region is changed.
34767          * @param {Roo.LayoutRegion} this
34768          */
34769         "invalidated" : true,
34770         /**
34771          * @event visibilitychange
34772          * Fires when this region is shown or hidden 
34773          * @param {Roo.LayoutRegion} this
34774          * @param {Boolean} visibility true or false
34775          */
34776         "visibilitychange" : true,
34777         /**
34778          * @event paneladded
34779          * Fires when a panel is added. 
34780          * @param {Roo.LayoutRegion} this
34781          * @param {Roo.ContentPanel} panel The panel
34782          */
34783         "paneladded" : true,
34784         /**
34785          * @event panelremoved
34786          * Fires when a panel is removed. 
34787          * @param {Roo.LayoutRegion} this
34788          * @param {Roo.ContentPanel} panel The panel
34789          */
34790         "panelremoved" : true,
34791         /**
34792          * @event beforecollapse
34793          * Fires when this region before collapse.
34794          * @param {Roo.LayoutRegion} this
34795          */
34796         "beforecollapse" : true,
34797         /**
34798          * @event collapsed
34799          * Fires when this region is collapsed.
34800          * @param {Roo.LayoutRegion} this
34801          */
34802         "collapsed" : true,
34803         /**
34804          * @event expanded
34805          * Fires when this region is expanded.
34806          * @param {Roo.LayoutRegion} this
34807          */
34808         "expanded" : true,
34809         /**
34810          * @event slideshow
34811          * Fires when this region is slid into view.
34812          * @param {Roo.LayoutRegion} this
34813          */
34814         "slideshow" : true,
34815         /**
34816          * @event slidehide
34817          * Fires when this region slides out of view. 
34818          * @param {Roo.LayoutRegion} this
34819          */
34820         "slidehide" : true,
34821         /**
34822          * @event panelactivated
34823          * Fires when a panel is activated. 
34824          * @param {Roo.LayoutRegion} this
34825          * @param {Roo.ContentPanel} panel The activated panel
34826          */
34827         "panelactivated" : true,
34828         /**
34829          * @event resized
34830          * Fires when the user resizes this region. 
34831          * @param {Roo.LayoutRegion} this
34832          * @param {Number} newSize The new size (width for east/west, height for north/south)
34833          */
34834         "resized" : true
34835     };
34836     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34837     this.panels = new Roo.util.MixedCollection();
34838     this.panels.getKey = this.getPanelId.createDelegate(this);
34839     this.box = null;
34840     this.activePanel = null;
34841     // ensure listeners are added...
34842     
34843     if (config.listeners || config.events) {
34844         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34845             listeners : config.listeners || {},
34846             events : config.events || {}
34847         });
34848     }
34849     
34850     if(skipConfig !== true){
34851         this.applyConfig(config);
34852     }
34853 };
34854
34855 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34856     getPanelId : function(p){
34857         return p.getId();
34858     },
34859     
34860     applyConfig : function(config){
34861         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34862         this.config = config;
34863         
34864     },
34865     
34866     /**
34867      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34868      * the width, for horizontal (north, south) the height.
34869      * @param {Number} newSize The new width or height
34870      */
34871     resizeTo : function(newSize){
34872         var el = this.el ? this.el :
34873                  (this.activePanel ? this.activePanel.getEl() : null);
34874         if(el){
34875             switch(this.position){
34876                 case "east":
34877                 case "west":
34878                     el.setWidth(newSize);
34879                     this.fireEvent("resized", this, newSize);
34880                 break;
34881                 case "north":
34882                 case "south":
34883                     el.setHeight(newSize);
34884                     this.fireEvent("resized", this, newSize);
34885                 break;                
34886             }
34887         }
34888     },
34889     
34890     getBox : function(){
34891         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34892     },
34893     
34894     getMargins : function(){
34895         return this.margins;
34896     },
34897     
34898     updateBox : function(box){
34899         this.box = box;
34900         var el = this.activePanel.getEl();
34901         el.dom.style.left = box.x + "px";
34902         el.dom.style.top = box.y + "px";
34903         this.activePanel.setSize(box.width, box.height);
34904     },
34905     
34906     /**
34907      * Returns the container element for this region.
34908      * @return {Roo.Element}
34909      */
34910     getEl : function(){
34911         return this.activePanel;
34912     },
34913     
34914     /**
34915      * Returns true if this region is currently visible.
34916      * @return {Boolean}
34917      */
34918     isVisible : function(){
34919         return this.activePanel ? true : false;
34920     },
34921     
34922     setActivePanel : function(panel){
34923         panel = this.getPanel(panel);
34924         if(this.activePanel && this.activePanel != panel){
34925             this.activePanel.setActiveState(false);
34926             this.activePanel.getEl().setLeftTop(-10000,-10000);
34927         }
34928         this.activePanel = panel;
34929         panel.setActiveState(true);
34930         if(this.box){
34931             panel.setSize(this.box.width, this.box.height);
34932         }
34933         this.fireEvent("panelactivated", this, panel);
34934         this.fireEvent("invalidated");
34935     },
34936     
34937     /**
34938      * Show the specified panel.
34939      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34940      * @return {Roo.ContentPanel} The shown panel or null
34941      */
34942     showPanel : function(panel){
34943         if(panel = this.getPanel(panel)){
34944             this.setActivePanel(panel);
34945         }
34946         return panel;
34947     },
34948     
34949     /**
34950      * Get the active panel for this region.
34951      * @return {Roo.ContentPanel} The active panel or null
34952      */
34953     getActivePanel : function(){
34954         return this.activePanel;
34955     },
34956     
34957     /**
34958      * Add the passed ContentPanel(s)
34959      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34960      * @return {Roo.ContentPanel} The panel added (if only one was added)
34961      */
34962     add : function(panel){
34963         if(arguments.length > 1){
34964             for(var i = 0, len = arguments.length; i < len; i++) {
34965                 this.add(arguments[i]);
34966             }
34967             return null;
34968         }
34969         if(this.hasPanel(panel)){
34970             this.showPanel(panel);
34971             return panel;
34972         }
34973         var el = panel.getEl();
34974         if(el.dom.parentNode != this.mgr.el.dom){
34975             this.mgr.el.dom.appendChild(el.dom);
34976         }
34977         if(panel.setRegion){
34978             panel.setRegion(this);
34979         }
34980         this.panels.add(panel);
34981         el.setStyle("position", "absolute");
34982         if(!panel.background){
34983             this.setActivePanel(panel);
34984             if(this.config.initialSize && this.panels.getCount()==1){
34985                 this.resizeTo(this.config.initialSize);
34986             }
34987         }
34988         this.fireEvent("paneladded", this, panel);
34989         return panel;
34990     },
34991     
34992     /**
34993      * Returns true if the panel is in this region.
34994      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34995      * @return {Boolean}
34996      */
34997     hasPanel : function(panel){
34998         if(typeof panel == "object"){ // must be panel obj
34999             panel = panel.getId();
35000         }
35001         return this.getPanel(panel) ? true : false;
35002     },
35003     
35004     /**
35005      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35006      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35007      * @param {Boolean} preservePanel Overrides the config preservePanel option
35008      * @return {Roo.ContentPanel} The panel that was removed
35009      */
35010     remove : function(panel, preservePanel){
35011         panel = this.getPanel(panel);
35012         if(!panel){
35013             return null;
35014         }
35015         var e = {};
35016         this.fireEvent("beforeremove", this, panel, e);
35017         if(e.cancel === true){
35018             return null;
35019         }
35020         var panelId = panel.getId();
35021         this.panels.removeKey(panelId);
35022         return panel;
35023     },
35024     
35025     /**
35026      * Returns the panel specified or null if it's not in this region.
35027      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35028      * @return {Roo.ContentPanel}
35029      */
35030     getPanel : function(id){
35031         if(typeof id == "object"){ // must be panel obj
35032             return id;
35033         }
35034         return this.panels.get(id);
35035     },
35036     
35037     /**
35038      * Returns this regions position (north/south/east/west/center).
35039      * @return {String} 
35040      */
35041     getPosition: function(){
35042         return this.position;    
35043     }
35044 });/*
35045  * Based on:
35046  * Ext JS Library 1.1.1
35047  * Copyright(c) 2006-2007, Ext JS, LLC.
35048  *
35049  * Originally Released Under LGPL - original licence link has changed is not relivant.
35050  *
35051  * Fork - LGPL
35052  * <script type="text/javascript">
35053  */
35054  
35055 /**
35056  * @class Roo.LayoutRegion
35057  * @extends Roo.BasicLayoutRegion
35058  * This class represents a region in a layout manager.
35059  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
35060  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
35061  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
35062  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35063  * @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})
35064  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35065  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
35066  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35067  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35068  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35069  * @cfg {String}    title           The title for the region (overrides panel titles)
35070  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35071  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35072  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35073  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35074  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35075  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35076  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35077  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35078  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35079  * @cfg {Boolean}   showPin         True to show a pin button
35080  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35081  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35082  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35083  * @cfg {Number}    width           For East/West panels
35084  * @cfg {Number}    height          For North/South panels
35085  * @cfg {Boolean}   split           To show the splitter
35086  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35087  */
35088 Roo.LayoutRegion = function(mgr, config, pos){
35089     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
35090     var dh = Roo.DomHelper;
35091     /** This region's container element 
35092     * @type Roo.Element */
35093     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
35094     /** This region's title element 
35095     * @type Roo.Element */
35096
35097     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
35098         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35099         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
35100     ]}, true);
35101     this.titleEl.enableDisplayMode();
35102     /** This region's title text element 
35103     * @type HTMLElement */
35104     this.titleTextEl = this.titleEl.dom.firstChild;
35105     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35106     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
35107     this.closeBtn.enableDisplayMode();
35108     this.closeBtn.on("click", this.closeClicked, this);
35109     this.closeBtn.hide();
35110
35111     this.createBody(config);
35112     this.visible = true;
35113     this.collapsed = false;
35114
35115     if(config.hideWhenEmpty){
35116         this.hide();
35117         this.on("paneladded", this.validateVisibility, this);
35118         this.on("panelremoved", this.validateVisibility, this);
35119     }
35120     this.applyConfig(config);
35121 };
35122
35123 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
35124
35125     createBody : function(){
35126         /** This region's body element 
35127         * @type Roo.Element */
35128         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
35129     },
35130
35131     applyConfig : function(c){
35132         if(c.collapsible && this.position != "center" && !this.collapsedEl){
35133             var dh = Roo.DomHelper;
35134             if(c.titlebar !== false){
35135                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
35136                 this.collapseBtn.on("click", this.collapse, this);
35137                 this.collapseBtn.enableDisplayMode();
35138
35139                 if(c.showPin === true || this.showPin){
35140                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
35141                     this.stickBtn.enableDisplayMode();
35142                     this.stickBtn.on("click", this.expand, this);
35143                     this.stickBtn.hide();
35144                 }
35145             }
35146             /** This region's collapsed element
35147             * @type Roo.Element */
35148             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35149                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35150             ]}, true);
35151             if(c.floatable !== false){
35152                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35153                this.collapsedEl.on("click", this.collapseClick, this);
35154             }
35155
35156             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35157                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35158                    id: "message", unselectable: "on", style:{"float":"left"}});
35159                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35160              }
35161             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35162             this.expandBtn.on("click", this.expand, this);
35163         }
35164         if(this.collapseBtn){
35165             this.collapseBtn.setVisible(c.collapsible == true);
35166         }
35167         this.cmargins = c.cmargins || this.cmargins ||
35168                          (this.position == "west" || this.position == "east" ?
35169                              {top: 0, left: 2, right:2, bottom: 0} :
35170                              {top: 2, left: 0, right:0, bottom: 2});
35171         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35172         this.bottomTabs = c.tabPosition != "top";
35173         this.autoScroll = c.autoScroll || false;
35174         if(this.autoScroll){
35175             this.bodyEl.setStyle("overflow", "auto");
35176         }else{
35177             this.bodyEl.setStyle("overflow", "hidden");
35178         }
35179         //if(c.titlebar !== false){
35180             if((!c.titlebar && !c.title) || c.titlebar === false){
35181                 this.titleEl.hide();
35182             }else{
35183                 this.titleEl.show();
35184                 if(c.title){
35185                     this.titleTextEl.innerHTML = c.title;
35186                 }
35187             }
35188         //}
35189         this.duration = c.duration || .30;
35190         this.slideDuration = c.slideDuration || .45;
35191         this.config = c;
35192         if(c.collapsed){
35193             this.collapse(true);
35194         }
35195         if(c.hidden){
35196             this.hide();
35197         }
35198     },
35199     /**
35200      * Returns true if this region is currently visible.
35201      * @return {Boolean}
35202      */
35203     isVisible : function(){
35204         return this.visible;
35205     },
35206
35207     /**
35208      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35209      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35210      */
35211     setCollapsedTitle : function(title){
35212         title = title || "&#160;";
35213         if(this.collapsedTitleTextEl){
35214             this.collapsedTitleTextEl.innerHTML = title;
35215         }
35216     },
35217
35218     getBox : function(){
35219         var b;
35220         if(!this.collapsed){
35221             b = this.el.getBox(false, true);
35222         }else{
35223             b = this.collapsedEl.getBox(false, true);
35224         }
35225         return b;
35226     },
35227
35228     getMargins : function(){
35229         return this.collapsed ? this.cmargins : this.margins;
35230     },
35231
35232     highlight : function(){
35233         this.el.addClass("x-layout-panel-dragover");
35234     },
35235
35236     unhighlight : function(){
35237         this.el.removeClass("x-layout-panel-dragover");
35238     },
35239
35240     updateBox : function(box){
35241         this.box = box;
35242         if(!this.collapsed){
35243             this.el.dom.style.left = box.x + "px";
35244             this.el.dom.style.top = box.y + "px";
35245             this.updateBody(box.width, box.height);
35246         }else{
35247             this.collapsedEl.dom.style.left = box.x + "px";
35248             this.collapsedEl.dom.style.top = box.y + "px";
35249             this.collapsedEl.setSize(box.width, box.height);
35250         }
35251         if(this.tabs){
35252             this.tabs.autoSizeTabs();
35253         }
35254     },
35255
35256     updateBody : function(w, h){
35257         if(w !== null){
35258             this.el.setWidth(w);
35259             w -= this.el.getBorderWidth("rl");
35260             if(this.config.adjustments){
35261                 w += this.config.adjustments[0];
35262             }
35263         }
35264         if(h !== null){
35265             this.el.setHeight(h);
35266             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35267             h -= this.el.getBorderWidth("tb");
35268             if(this.config.adjustments){
35269                 h += this.config.adjustments[1];
35270             }
35271             this.bodyEl.setHeight(h);
35272             if(this.tabs){
35273                 h = this.tabs.syncHeight(h);
35274             }
35275         }
35276         if(this.panelSize){
35277             w = w !== null ? w : this.panelSize.width;
35278             h = h !== null ? h : this.panelSize.height;
35279         }
35280         if(this.activePanel){
35281             var el = this.activePanel.getEl();
35282             w = w !== null ? w : el.getWidth();
35283             h = h !== null ? h : el.getHeight();
35284             this.panelSize = {width: w, height: h};
35285             this.activePanel.setSize(w, h);
35286         }
35287         if(Roo.isIE && this.tabs){
35288             this.tabs.el.repaint();
35289         }
35290     },
35291
35292     /**
35293      * Returns the container element for this region.
35294      * @return {Roo.Element}
35295      */
35296     getEl : function(){
35297         return this.el;
35298     },
35299
35300     /**
35301      * Hides this region.
35302      */
35303     hide : function(){
35304         if(!this.collapsed){
35305             this.el.dom.style.left = "-2000px";
35306             this.el.hide();
35307         }else{
35308             this.collapsedEl.dom.style.left = "-2000px";
35309             this.collapsedEl.hide();
35310         }
35311         this.visible = false;
35312         this.fireEvent("visibilitychange", this, false);
35313     },
35314
35315     /**
35316      * Shows this region if it was previously hidden.
35317      */
35318     show : function(){
35319         if(!this.collapsed){
35320             this.el.show();
35321         }else{
35322             this.collapsedEl.show();
35323         }
35324         this.visible = true;
35325         this.fireEvent("visibilitychange", this, true);
35326     },
35327
35328     closeClicked : function(){
35329         if(this.activePanel){
35330             this.remove(this.activePanel);
35331         }
35332     },
35333
35334     collapseClick : function(e){
35335         if(this.isSlid){
35336            e.stopPropagation();
35337            this.slideIn();
35338         }else{
35339            e.stopPropagation();
35340            this.slideOut();
35341         }
35342     },
35343
35344     /**
35345      * Collapses this region.
35346      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35347      */
35348     collapse : function(skipAnim, skipCheck){
35349         if(this.collapsed) {
35350             return;
35351         }
35352         
35353         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35354             
35355             this.collapsed = true;
35356             if(this.split){
35357                 this.split.el.hide();
35358             }
35359             if(this.config.animate && skipAnim !== true){
35360                 this.fireEvent("invalidated", this);
35361                 this.animateCollapse();
35362             }else{
35363                 this.el.setLocation(-20000,-20000);
35364                 this.el.hide();
35365                 this.collapsedEl.show();
35366                 this.fireEvent("collapsed", this);
35367                 this.fireEvent("invalidated", this);
35368             }
35369         }
35370         
35371     },
35372
35373     animateCollapse : function(){
35374         // overridden
35375     },
35376
35377     /**
35378      * Expands this region if it was previously collapsed.
35379      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35380      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35381      */
35382     expand : function(e, skipAnim){
35383         if(e) {
35384             e.stopPropagation();
35385         }
35386         if(!this.collapsed || this.el.hasActiveFx()) {
35387             return;
35388         }
35389         if(this.isSlid){
35390             this.afterSlideIn();
35391             skipAnim = true;
35392         }
35393         this.collapsed = false;
35394         if(this.config.animate && skipAnim !== true){
35395             this.animateExpand();
35396         }else{
35397             this.el.show();
35398             if(this.split){
35399                 this.split.el.show();
35400             }
35401             this.collapsedEl.setLocation(-2000,-2000);
35402             this.collapsedEl.hide();
35403             this.fireEvent("invalidated", this);
35404             this.fireEvent("expanded", this);
35405         }
35406     },
35407
35408     animateExpand : function(){
35409         // overridden
35410     },
35411
35412     initTabs : function()
35413     {
35414         this.bodyEl.setStyle("overflow", "hidden");
35415         var ts = new Roo.TabPanel(
35416                 this.bodyEl.dom,
35417                 {
35418                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
35419                     disableTooltips: this.config.disableTabTips,
35420                     toolbar : this.config.toolbar
35421                 }
35422         );
35423         if(this.config.hideTabs){
35424             ts.stripWrap.setDisplayed(false);
35425         }
35426         this.tabs = ts;
35427         ts.resizeTabs = this.config.resizeTabs === true;
35428         ts.minTabWidth = this.config.minTabWidth || 40;
35429         ts.maxTabWidth = this.config.maxTabWidth || 250;
35430         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35431         ts.monitorResize = false;
35432         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35433         ts.bodyEl.addClass('x-layout-tabs-body');
35434         this.panels.each(this.initPanelAsTab, this);
35435     },
35436
35437     initPanelAsTab : function(panel){
35438         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
35439                     this.config.closeOnTab && panel.isClosable());
35440         if(panel.tabTip !== undefined){
35441             ti.setTooltip(panel.tabTip);
35442         }
35443         ti.on("activate", function(){
35444               this.setActivePanel(panel);
35445         }, this);
35446         if(this.config.closeOnTab){
35447             ti.on("beforeclose", function(t, e){
35448                 e.cancel = true;
35449                 this.remove(panel);
35450             }, this);
35451         }
35452         return ti;
35453     },
35454
35455     updatePanelTitle : function(panel, title){
35456         if(this.activePanel == panel){
35457             this.updateTitle(title);
35458         }
35459         if(this.tabs){
35460             var ti = this.tabs.getTab(panel.getEl().id);
35461             ti.setText(title);
35462             if(panel.tabTip !== undefined){
35463                 ti.setTooltip(panel.tabTip);
35464             }
35465         }
35466     },
35467
35468     updateTitle : function(title){
35469         if(this.titleTextEl && !this.config.title){
35470             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35471         }
35472     },
35473
35474     setActivePanel : function(panel){
35475         panel = this.getPanel(panel);
35476         if(this.activePanel && this.activePanel != panel){
35477             this.activePanel.setActiveState(false);
35478         }
35479         this.activePanel = panel;
35480         panel.setActiveState(true);
35481         if(this.panelSize){
35482             panel.setSize(this.panelSize.width, this.panelSize.height);
35483         }
35484         if(this.closeBtn){
35485             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35486         }
35487         this.updateTitle(panel.getTitle());
35488         if(this.tabs){
35489             this.fireEvent("invalidated", this);
35490         }
35491         this.fireEvent("panelactivated", this, panel);
35492     },
35493
35494     /**
35495      * Shows the specified panel.
35496      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35497      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35498      */
35499     showPanel : function(panel)
35500     {
35501         panel = this.getPanel(panel);
35502         if(panel){
35503             if(this.tabs){
35504                 var tab = this.tabs.getTab(panel.getEl().id);
35505                 if(tab.isHidden()){
35506                     this.tabs.unhideTab(tab.id);
35507                 }
35508                 tab.activate();
35509             }else{
35510                 this.setActivePanel(panel);
35511             }
35512         }
35513         return panel;
35514     },
35515
35516     /**
35517      * Get the active panel for this region.
35518      * @return {Roo.ContentPanel} The active panel or null
35519      */
35520     getActivePanel : function(){
35521         return this.activePanel;
35522     },
35523
35524     validateVisibility : function(){
35525         if(this.panels.getCount() < 1){
35526             this.updateTitle("&#160;");
35527             this.closeBtn.hide();
35528             this.hide();
35529         }else{
35530             if(!this.isVisible()){
35531                 this.show();
35532             }
35533         }
35534     },
35535
35536     /**
35537      * Adds the passed ContentPanel(s) to this region.
35538      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35539      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35540      */
35541     add : function(panel){
35542         if(arguments.length > 1){
35543             for(var i = 0, len = arguments.length; i < len; i++) {
35544                 this.add(arguments[i]);
35545             }
35546             return null;
35547         }
35548         if(this.hasPanel(panel)){
35549             this.showPanel(panel);
35550             return panel;
35551         }
35552         panel.setRegion(this);
35553         this.panels.add(panel);
35554         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35555             this.bodyEl.dom.appendChild(panel.getEl().dom);
35556             if(panel.background !== true){
35557                 this.setActivePanel(panel);
35558             }
35559             this.fireEvent("paneladded", this, panel);
35560             return panel;
35561         }
35562         if(!this.tabs){
35563             this.initTabs();
35564         }else{
35565             this.initPanelAsTab(panel);
35566         }
35567         if(panel.background !== true){
35568             this.tabs.activate(panel.getEl().id);
35569         }
35570         this.fireEvent("paneladded", this, panel);
35571         return panel;
35572     },
35573
35574     /**
35575      * Hides the tab for the specified panel.
35576      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35577      */
35578     hidePanel : function(panel){
35579         if(this.tabs && (panel = this.getPanel(panel))){
35580             this.tabs.hideTab(panel.getEl().id);
35581         }
35582     },
35583
35584     /**
35585      * Unhides the tab for a previously hidden panel.
35586      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35587      */
35588     unhidePanel : function(panel){
35589         if(this.tabs && (panel = this.getPanel(panel))){
35590             this.tabs.unhideTab(panel.getEl().id);
35591         }
35592     },
35593
35594     clearPanels : function(){
35595         while(this.panels.getCount() > 0){
35596              this.remove(this.panels.first());
35597         }
35598     },
35599
35600     /**
35601      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35602      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35603      * @param {Boolean} preservePanel Overrides the config preservePanel option
35604      * @return {Roo.ContentPanel} The panel that was removed
35605      */
35606     remove : function(panel, preservePanel){
35607         panel = this.getPanel(panel);
35608         if(!panel){
35609             return null;
35610         }
35611         var e = {};
35612         this.fireEvent("beforeremove", this, panel, e);
35613         if(e.cancel === true){
35614             return null;
35615         }
35616         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35617         var panelId = panel.getId();
35618         this.panels.removeKey(panelId);
35619         if(preservePanel){
35620             document.body.appendChild(panel.getEl().dom);
35621         }
35622         if(this.tabs){
35623             this.tabs.removeTab(panel.getEl().id);
35624         }else if (!preservePanel){
35625             this.bodyEl.dom.removeChild(panel.getEl().dom);
35626         }
35627         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35628             var p = this.panels.first();
35629             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35630             tempEl.appendChild(p.getEl().dom);
35631             this.bodyEl.update("");
35632             this.bodyEl.dom.appendChild(p.getEl().dom);
35633             tempEl = null;
35634             this.updateTitle(p.getTitle());
35635             this.tabs = null;
35636             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35637             this.setActivePanel(p);
35638         }
35639         panel.setRegion(null);
35640         if(this.activePanel == panel){
35641             this.activePanel = null;
35642         }
35643         if(this.config.autoDestroy !== false && preservePanel !== true){
35644             try{panel.destroy();}catch(e){}
35645         }
35646         this.fireEvent("panelremoved", this, panel);
35647         return panel;
35648     },
35649
35650     /**
35651      * Returns the TabPanel component used by this region
35652      * @return {Roo.TabPanel}
35653      */
35654     getTabs : function(){
35655         return this.tabs;
35656     },
35657
35658     createTool : function(parentEl, className){
35659         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35660             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
35661         btn.addClassOnOver("x-layout-tools-button-over");
35662         return btn;
35663     }
35664 });/*
35665  * Based on:
35666  * Ext JS Library 1.1.1
35667  * Copyright(c) 2006-2007, Ext JS, LLC.
35668  *
35669  * Originally Released Under LGPL - original licence link has changed is not relivant.
35670  *
35671  * Fork - LGPL
35672  * <script type="text/javascript">
35673  */
35674  
35675
35676
35677 /**
35678  * @class Roo.SplitLayoutRegion
35679  * @extends Roo.LayoutRegion
35680  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35681  */
35682 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35683     this.cursor = cursor;
35684     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35685 };
35686
35687 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35688     splitTip : "Drag to resize.",
35689     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35690     useSplitTips : false,
35691
35692     applyConfig : function(config){
35693         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35694         if(config.split){
35695             if(!this.split){
35696                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
35697                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
35698                 /** The SplitBar for this region 
35699                 * @type Roo.SplitBar */
35700                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35701                 this.split.on("moved", this.onSplitMove, this);
35702                 this.split.useShim = config.useShim === true;
35703                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35704                 if(this.useSplitTips){
35705                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35706                 }
35707                 if(config.collapsible){
35708                     this.split.el.on("dblclick", this.collapse,  this);
35709                 }
35710             }
35711             if(typeof config.minSize != "undefined"){
35712                 this.split.minSize = config.minSize;
35713             }
35714             if(typeof config.maxSize != "undefined"){
35715                 this.split.maxSize = config.maxSize;
35716             }
35717             if(config.hideWhenEmpty || config.hidden || config.collapsed){
35718                 this.hideSplitter();
35719             }
35720         }
35721     },
35722
35723     getHMaxSize : function(){
35724          var cmax = this.config.maxSize || 10000;
35725          var center = this.mgr.getRegion("center");
35726          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35727     },
35728
35729     getVMaxSize : function(){
35730          var cmax = this.config.maxSize || 10000;
35731          var center = this.mgr.getRegion("center");
35732          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35733     },
35734
35735     onSplitMove : function(split, newSize){
35736         this.fireEvent("resized", this, newSize);
35737     },
35738     
35739     /** 
35740      * Returns the {@link Roo.SplitBar} for this region.
35741      * @return {Roo.SplitBar}
35742      */
35743     getSplitBar : function(){
35744         return this.split;
35745     },
35746     
35747     hide : function(){
35748         this.hideSplitter();
35749         Roo.SplitLayoutRegion.superclass.hide.call(this);
35750     },
35751
35752     hideSplitter : function(){
35753         if(this.split){
35754             this.split.el.setLocation(-2000,-2000);
35755             this.split.el.hide();
35756         }
35757     },
35758
35759     show : function(){
35760         if(this.split){
35761             this.split.el.show();
35762         }
35763         Roo.SplitLayoutRegion.superclass.show.call(this);
35764     },
35765     
35766     beforeSlide: function(){
35767         if(Roo.isGecko){// firefox overflow auto bug workaround
35768             this.bodyEl.clip();
35769             if(this.tabs) {
35770                 this.tabs.bodyEl.clip();
35771             }
35772             if(this.activePanel){
35773                 this.activePanel.getEl().clip();
35774                 
35775                 if(this.activePanel.beforeSlide){
35776                     this.activePanel.beforeSlide();
35777                 }
35778             }
35779         }
35780     },
35781     
35782     afterSlide : function(){
35783         if(Roo.isGecko){// firefox overflow auto bug workaround
35784             this.bodyEl.unclip();
35785             if(this.tabs) {
35786                 this.tabs.bodyEl.unclip();
35787             }
35788             if(this.activePanel){
35789                 this.activePanel.getEl().unclip();
35790                 if(this.activePanel.afterSlide){
35791                     this.activePanel.afterSlide();
35792                 }
35793             }
35794         }
35795     },
35796
35797     initAutoHide : function(){
35798         if(this.autoHide !== false){
35799             if(!this.autoHideHd){
35800                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35801                 this.autoHideHd = {
35802                     "mouseout": function(e){
35803                         if(!e.within(this.el, true)){
35804                             st.delay(500);
35805                         }
35806                     },
35807                     "mouseover" : function(e){
35808                         st.cancel();
35809                     },
35810                     scope : this
35811                 };
35812             }
35813             this.el.on(this.autoHideHd);
35814         }
35815     },
35816
35817     clearAutoHide : function(){
35818         if(this.autoHide !== false){
35819             this.el.un("mouseout", this.autoHideHd.mouseout);
35820             this.el.un("mouseover", this.autoHideHd.mouseover);
35821         }
35822     },
35823
35824     clearMonitor : function(){
35825         Roo.get(document).un("click", this.slideInIf, this);
35826     },
35827
35828     // these names are backwards but not changed for compat
35829     slideOut : function(){
35830         if(this.isSlid || this.el.hasActiveFx()){
35831             return;
35832         }
35833         this.isSlid = true;
35834         if(this.collapseBtn){
35835             this.collapseBtn.hide();
35836         }
35837         this.closeBtnState = this.closeBtn.getStyle('display');
35838         this.closeBtn.hide();
35839         if(this.stickBtn){
35840             this.stickBtn.show();
35841         }
35842         this.el.show();
35843         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35844         this.beforeSlide();
35845         this.el.setStyle("z-index", 10001);
35846         this.el.slideIn(this.getSlideAnchor(), {
35847             callback: function(){
35848                 this.afterSlide();
35849                 this.initAutoHide();
35850                 Roo.get(document).on("click", this.slideInIf, this);
35851                 this.fireEvent("slideshow", this);
35852             },
35853             scope: this,
35854             block: true
35855         });
35856     },
35857
35858     afterSlideIn : function(){
35859         this.clearAutoHide();
35860         this.isSlid = false;
35861         this.clearMonitor();
35862         this.el.setStyle("z-index", "");
35863         if(this.collapseBtn){
35864             this.collapseBtn.show();
35865         }
35866         this.closeBtn.setStyle('display', this.closeBtnState);
35867         if(this.stickBtn){
35868             this.stickBtn.hide();
35869         }
35870         this.fireEvent("slidehide", this);
35871     },
35872
35873     slideIn : function(cb){
35874         if(!this.isSlid || this.el.hasActiveFx()){
35875             Roo.callback(cb);
35876             return;
35877         }
35878         this.isSlid = false;
35879         this.beforeSlide();
35880         this.el.slideOut(this.getSlideAnchor(), {
35881             callback: function(){
35882                 this.el.setLeftTop(-10000, -10000);
35883                 this.afterSlide();
35884                 this.afterSlideIn();
35885                 Roo.callback(cb);
35886             },
35887             scope: this,
35888             block: true
35889         });
35890     },
35891     
35892     slideInIf : function(e){
35893         if(!e.within(this.el)){
35894             this.slideIn();
35895         }
35896     },
35897
35898     animateCollapse : function(){
35899         this.beforeSlide();
35900         this.el.setStyle("z-index", 20000);
35901         var anchor = this.getSlideAnchor();
35902         this.el.slideOut(anchor, {
35903             callback : function(){
35904                 this.el.setStyle("z-index", "");
35905                 this.collapsedEl.slideIn(anchor, {duration:.3});
35906                 this.afterSlide();
35907                 this.el.setLocation(-10000,-10000);
35908                 this.el.hide();
35909                 this.fireEvent("collapsed", this);
35910             },
35911             scope: this,
35912             block: true
35913         });
35914     },
35915
35916     animateExpand : function(){
35917         this.beforeSlide();
35918         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35919         this.el.setStyle("z-index", 20000);
35920         this.collapsedEl.hide({
35921             duration:.1
35922         });
35923         this.el.slideIn(this.getSlideAnchor(), {
35924             callback : function(){
35925                 this.el.setStyle("z-index", "");
35926                 this.afterSlide();
35927                 if(this.split){
35928                     this.split.el.show();
35929                 }
35930                 this.fireEvent("invalidated", this);
35931                 this.fireEvent("expanded", this);
35932             },
35933             scope: this,
35934             block: true
35935         });
35936     },
35937
35938     anchors : {
35939         "west" : "left",
35940         "east" : "right",
35941         "north" : "top",
35942         "south" : "bottom"
35943     },
35944
35945     sanchors : {
35946         "west" : "l",
35947         "east" : "r",
35948         "north" : "t",
35949         "south" : "b"
35950     },
35951
35952     canchors : {
35953         "west" : "tl-tr",
35954         "east" : "tr-tl",
35955         "north" : "tl-bl",
35956         "south" : "bl-tl"
35957     },
35958
35959     getAnchor : function(){
35960         return this.anchors[this.position];
35961     },
35962
35963     getCollapseAnchor : function(){
35964         return this.canchors[this.position];
35965     },
35966
35967     getSlideAnchor : function(){
35968         return this.sanchors[this.position];
35969     },
35970
35971     getAlignAdj : function(){
35972         var cm = this.cmargins;
35973         switch(this.position){
35974             case "west":
35975                 return [0, 0];
35976             break;
35977             case "east":
35978                 return [0, 0];
35979             break;
35980             case "north":
35981                 return [0, 0];
35982             break;
35983             case "south":
35984                 return [0, 0];
35985             break;
35986         }
35987     },
35988
35989     getExpandAdj : function(){
35990         var c = this.collapsedEl, cm = this.cmargins;
35991         switch(this.position){
35992             case "west":
35993                 return [-(cm.right+c.getWidth()+cm.left), 0];
35994             break;
35995             case "east":
35996                 return [cm.right+c.getWidth()+cm.left, 0];
35997             break;
35998             case "north":
35999                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36000             break;
36001             case "south":
36002                 return [0, cm.top+cm.bottom+c.getHeight()];
36003             break;
36004         }
36005     }
36006 });/*
36007  * Based on:
36008  * Ext JS Library 1.1.1
36009  * Copyright(c) 2006-2007, Ext JS, LLC.
36010  *
36011  * Originally Released Under LGPL - original licence link has changed is not relivant.
36012  *
36013  * Fork - LGPL
36014  * <script type="text/javascript">
36015  */
36016 /*
36017  * These classes are private internal classes
36018  */
36019 Roo.CenterLayoutRegion = function(mgr, config){
36020     Roo.LayoutRegion.call(this, mgr, config, "center");
36021     this.visible = true;
36022     this.minWidth = config.minWidth || 20;
36023     this.minHeight = config.minHeight || 20;
36024 };
36025
36026 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
36027     hide : function(){
36028         // center panel can't be hidden
36029     },
36030     
36031     show : function(){
36032         // center panel can't be hidden
36033     },
36034     
36035     getMinWidth: function(){
36036         return this.minWidth;
36037     },
36038     
36039     getMinHeight: function(){
36040         return this.minHeight;
36041     }
36042 });
36043
36044
36045 Roo.NorthLayoutRegion = function(mgr, config){
36046     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
36047     if(this.split){
36048         this.split.placement = Roo.SplitBar.TOP;
36049         this.split.orientation = Roo.SplitBar.VERTICAL;
36050         this.split.el.addClass("x-layout-split-v");
36051     }
36052     var size = config.initialSize || config.height;
36053     if(typeof size != "undefined"){
36054         this.el.setHeight(size);
36055     }
36056 };
36057 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
36058     orientation: Roo.SplitBar.VERTICAL,
36059     getBox : function(){
36060         if(this.collapsed){
36061             return this.collapsedEl.getBox();
36062         }
36063         var box = this.el.getBox();
36064         if(this.split){
36065             box.height += this.split.el.getHeight();
36066         }
36067         return box;
36068     },
36069     
36070     updateBox : function(box){
36071         if(this.split && !this.collapsed){
36072             box.height -= this.split.el.getHeight();
36073             this.split.el.setLeft(box.x);
36074             this.split.el.setTop(box.y+box.height);
36075             this.split.el.setWidth(box.width);
36076         }
36077         if(this.collapsed){
36078             this.updateBody(box.width, null);
36079         }
36080         Roo.LayoutRegion.prototype.updateBox.call(this, box);
36081     }
36082 });
36083
36084 Roo.SouthLayoutRegion = function(mgr, config){
36085     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
36086     if(this.split){
36087         this.split.placement = Roo.SplitBar.BOTTOM;
36088         this.split.orientation = Roo.SplitBar.VERTICAL;
36089         this.split.el.addClass("x-layout-split-v");
36090     }
36091     var size = config.initialSize || config.height;
36092     if(typeof size != "undefined"){
36093         this.el.setHeight(size);
36094     }
36095 };
36096 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
36097     orientation: Roo.SplitBar.VERTICAL,
36098     getBox : function(){
36099         if(this.collapsed){
36100             return this.collapsedEl.getBox();
36101         }
36102         var box = this.el.getBox();
36103         if(this.split){
36104             var sh = this.split.el.getHeight();
36105             box.height += sh;
36106             box.y -= sh;
36107         }
36108         return box;
36109     },
36110     
36111     updateBox : function(box){
36112         if(this.split && !this.collapsed){
36113             var sh = this.split.el.getHeight();
36114             box.height -= sh;
36115             box.y += sh;
36116             this.split.el.setLeft(box.x);
36117             this.split.el.setTop(box.y-sh);
36118             this.split.el.setWidth(box.width);
36119         }
36120         if(this.collapsed){
36121             this.updateBody(box.width, null);
36122         }
36123         Roo.LayoutRegion.prototype.updateBox.call(this, box);
36124     }
36125 });
36126
36127 Roo.EastLayoutRegion = function(mgr, config){
36128     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
36129     if(this.split){
36130         this.split.placement = Roo.SplitBar.RIGHT;
36131         this.split.orientation = Roo.SplitBar.HORIZONTAL;
36132         this.split.el.addClass("x-layout-split-h");
36133     }
36134     var size = config.initialSize || config.width;
36135     if(typeof size != "undefined"){
36136         this.el.setWidth(size);
36137     }
36138 };
36139 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
36140     orientation: Roo.SplitBar.HORIZONTAL,
36141     getBox : function(){
36142         if(this.collapsed){
36143             return this.collapsedEl.getBox();
36144         }
36145         var box = this.el.getBox();
36146         if(this.split){
36147             var sw = this.split.el.getWidth();
36148             box.width += sw;
36149             box.x -= sw;
36150         }
36151         return box;
36152     },
36153
36154     updateBox : function(box){
36155         if(this.split && !this.collapsed){
36156             var sw = this.split.el.getWidth();
36157             box.width -= sw;
36158             this.split.el.setLeft(box.x);
36159             this.split.el.setTop(box.y);
36160             this.split.el.setHeight(box.height);
36161             box.x += sw;
36162         }
36163         if(this.collapsed){
36164             this.updateBody(null, box.height);
36165         }
36166         Roo.LayoutRegion.prototype.updateBox.call(this, box);
36167     }
36168 });
36169
36170 Roo.WestLayoutRegion = function(mgr, config){
36171     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
36172     if(this.split){
36173         this.split.placement = Roo.SplitBar.LEFT;
36174         this.split.orientation = Roo.SplitBar.HORIZONTAL;
36175         this.split.el.addClass("x-layout-split-h");
36176     }
36177     var size = config.initialSize || config.width;
36178     if(typeof size != "undefined"){
36179         this.el.setWidth(size);
36180     }
36181 };
36182 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
36183     orientation: Roo.SplitBar.HORIZONTAL,
36184     getBox : function(){
36185         if(this.collapsed){
36186             return this.collapsedEl.getBox();
36187         }
36188         var box = this.el.getBox();
36189         if(this.split){
36190             box.width += this.split.el.getWidth();
36191         }
36192         return box;
36193     },
36194     
36195     updateBox : function(box){
36196         if(this.split && !this.collapsed){
36197             var sw = this.split.el.getWidth();
36198             box.width -= sw;
36199             this.split.el.setLeft(box.x+box.width);
36200             this.split.el.setTop(box.y);
36201             this.split.el.setHeight(box.height);
36202         }
36203         if(this.collapsed){
36204             this.updateBody(null, box.height);
36205         }
36206         Roo.LayoutRegion.prototype.updateBox.call(this, box);
36207     }
36208 });
36209 /*
36210  * Based on:
36211  * Ext JS Library 1.1.1
36212  * Copyright(c) 2006-2007, Ext JS, LLC.
36213  *
36214  * Originally Released Under LGPL - original licence link has changed is not relivant.
36215  *
36216  * Fork - LGPL
36217  * <script type="text/javascript">
36218  */
36219  
36220  
36221 /*
36222  * Private internal class for reading and applying state
36223  */
36224 Roo.LayoutStateManager = function(layout){
36225      // default empty state
36226      this.state = {
36227         north: {},
36228         south: {},
36229         east: {},
36230         west: {}       
36231     };
36232 };
36233
36234 Roo.LayoutStateManager.prototype = {
36235     init : function(layout, provider){
36236         this.provider = provider;
36237         var state = provider.get(layout.id+"-layout-state");
36238         if(state){
36239             var wasUpdating = layout.isUpdating();
36240             if(!wasUpdating){
36241                 layout.beginUpdate();
36242             }
36243             for(var key in state){
36244                 if(typeof state[key] != "function"){
36245                     var rstate = state[key];
36246                     var r = layout.getRegion(key);
36247                     if(r && rstate){
36248                         if(rstate.size){
36249                             r.resizeTo(rstate.size);
36250                         }
36251                         if(rstate.collapsed == true){
36252                             r.collapse(true);
36253                         }else{
36254                             r.expand(null, true);
36255                         }
36256                     }
36257                 }
36258             }
36259             if(!wasUpdating){
36260                 layout.endUpdate();
36261             }
36262             this.state = state; 
36263         }
36264         this.layout = layout;
36265         layout.on("regionresized", this.onRegionResized, this);
36266         layout.on("regioncollapsed", this.onRegionCollapsed, this);
36267         layout.on("regionexpanded", this.onRegionExpanded, this);
36268     },
36269     
36270     storeState : function(){
36271         this.provider.set(this.layout.id+"-layout-state", this.state);
36272     },
36273     
36274     onRegionResized : function(region, newSize){
36275         this.state[region.getPosition()].size = newSize;
36276         this.storeState();
36277     },
36278     
36279     onRegionCollapsed : function(region){
36280         this.state[region.getPosition()].collapsed = true;
36281         this.storeState();
36282     },
36283     
36284     onRegionExpanded : function(region){
36285         this.state[region.getPosition()].collapsed = false;
36286         this.storeState();
36287     }
36288 };/*
36289  * Based on:
36290  * Ext JS Library 1.1.1
36291  * Copyright(c) 2006-2007, Ext JS, LLC.
36292  *
36293  * Originally Released Under LGPL - original licence link has changed is not relivant.
36294  *
36295  * Fork - LGPL
36296  * <script type="text/javascript">
36297  */
36298 /**
36299  * @class Roo.ContentPanel
36300  * @extends Roo.util.Observable
36301  * @children Roo.form.Form Roo.JsonView Roo.View
36302  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36303  * A basic ContentPanel element.
36304  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36305  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36306  * @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
36307  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36308  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36309  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36310  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
36311  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36312  * @cfg {String} title          The title for this panel
36313  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36314  * @cfg {String} url            Calls {@link #setUrl} with this value
36315  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
36316  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36317  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36318  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36319  * @cfg {String}    style  Extra style to add to the content panel
36320  * @cfg {Roo.menu.Menu} menu  popup menu
36321
36322  * @constructor
36323  * Create a new ContentPanel.
36324  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36325  * @param {String/Object} config A string to set only the title or a config object
36326  * @param {String} content (optional) Set the HTML content for this panel
36327  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36328  */
36329 Roo.ContentPanel = function(el, config, content){
36330     
36331     /*
36332     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
36333         config = el;
36334         el = Roo.id();
36335     }
36336     if (config && config.parentLayout) { 
36337         el = config.parentLayout.el.createChild(); 
36338     }
36339     */
36340     if(el.autoCreate){ // xtype is available if this is called from factory
36341         config = el;
36342         el = Roo.id();
36343     }
36344     this.el = Roo.get(el);
36345     if(!this.el && config && config.autoCreate){
36346         if(typeof config.autoCreate == "object"){
36347             if(!config.autoCreate.id){
36348                 config.autoCreate.id = config.id||el;
36349             }
36350             this.el = Roo.DomHelper.append(document.body,
36351                         config.autoCreate, true);
36352         }else{
36353             this.el = Roo.DomHelper.append(document.body,
36354                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
36355         }
36356     }
36357     
36358     
36359     this.closable = false;
36360     this.loaded = false;
36361     this.active = false;
36362     if(typeof config == "string"){
36363         this.title = config;
36364     }else{
36365         Roo.apply(this, config);
36366     }
36367     
36368     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
36369         this.wrapEl = this.el.wrap();
36370         this.toolbar.container = this.el.insertSibling(false, 'before');
36371         this.toolbar = new Roo.Toolbar(this.toolbar);
36372     }
36373     
36374     // xtype created footer. - not sure if will work as we normally have to render first..
36375     if (this.footer && !this.footer.el && this.footer.xtype) {
36376         if (!this.wrapEl) {
36377             this.wrapEl = this.el.wrap();
36378         }
36379     
36380         this.footer.container = this.wrapEl.createChild();
36381          
36382         this.footer = Roo.factory(this.footer, Roo);
36383         
36384     }
36385     
36386     if(this.resizeEl){
36387         this.resizeEl = Roo.get(this.resizeEl, true);
36388     }else{
36389         this.resizeEl = this.el;
36390     }
36391     // handle view.xtype
36392     
36393  
36394     
36395     
36396     this.addEvents({
36397         /**
36398          * @event activate
36399          * Fires when this panel is activated. 
36400          * @param {Roo.ContentPanel} this
36401          */
36402         "activate" : true,
36403         /**
36404          * @event deactivate
36405          * Fires when this panel is activated. 
36406          * @param {Roo.ContentPanel} this
36407          */
36408         "deactivate" : true,
36409
36410         /**
36411          * @event resize
36412          * Fires when this panel is resized if fitToFrame is true.
36413          * @param {Roo.ContentPanel} this
36414          * @param {Number} width The width after any component adjustments
36415          * @param {Number} height The height after any component adjustments
36416          */
36417         "resize" : true,
36418         
36419          /**
36420          * @event render
36421          * Fires when this tab is created
36422          * @param {Roo.ContentPanel} this
36423          */
36424         "render" : true
36425          
36426         
36427     });
36428     
36429
36430     
36431     
36432     if(this.autoScroll){
36433         this.resizeEl.setStyle("overflow", "auto");
36434     } else {
36435         // fix randome scrolling
36436         this.el.on('scroll', function() {
36437             Roo.log('fix random scolling');
36438             this.scrollTo('top',0); 
36439         });
36440     }
36441     content = content || this.content;
36442     if(content){
36443         this.setContent(content);
36444     }
36445     if(config && config.url){
36446         this.setUrl(this.url, this.params, this.loadOnce);
36447     }
36448     
36449     
36450     
36451     Roo.ContentPanel.superclass.constructor.call(this);
36452     
36453     if (this.view && typeof(this.view.xtype) != 'undefined') {
36454         this.view.el = this.el.appendChild(document.createElement("div"));
36455         this.view = Roo.factory(this.view); 
36456         this.view.render  &&  this.view.render(false, '');  
36457     }
36458     
36459     
36460     this.fireEvent('render', this);
36461 };
36462
36463 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
36464     tabTip:'',
36465     setRegion : function(region){
36466         this.region = region;
36467         if(region){
36468            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
36469         }else{
36470            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
36471         } 
36472     },
36473     
36474     /**
36475      * Returns the toolbar for this Panel if one was configured. 
36476      * @return {Roo.Toolbar} 
36477      */
36478     getToolbar : function(){
36479         return this.toolbar;
36480     },
36481     
36482     setActiveState : function(active){
36483         this.active = active;
36484         if(!active){
36485             this.fireEvent("deactivate", this);
36486         }else{
36487             this.fireEvent("activate", this);
36488         }
36489     },
36490     /**
36491      * Updates this panel's element
36492      * @param {String} content The new content
36493      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36494     */
36495     setContent : function(content, loadScripts){
36496         this.el.update(content, loadScripts);
36497     },
36498
36499     ignoreResize : function(w, h){
36500         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36501             return true;
36502         }else{
36503             this.lastSize = {width: w, height: h};
36504             return false;
36505         }
36506     },
36507     /**
36508      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36509      * @return {Roo.UpdateManager} The UpdateManager
36510      */
36511     getUpdateManager : function(){
36512         return this.el.getUpdateManager();
36513     },
36514      /**
36515      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36516      * @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:
36517 <pre><code>
36518 panel.load({
36519     url: "your-url.php",
36520     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36521     callback: yourFunction,
36522     scope: yourObject, //(optional scope)
36523     discardUrl: false,
36524     nocache: false,
36525     text: "Loading...",
36526     timeout: 30,
36527     scripts: false
36528 });
36529 </code></pre>
36530      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36531      * 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.
36532      * @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}
36533      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36534      * @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.
36535      * @return {Roo.ContentPanel} this
36536      */
36537     load : function(){
36538         var um = this.el.getUpdateManager();
36539         um.update.apply(um, arguments);
36540         return this;
36541     },
36542
36543
36544     /**
36545      * 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.
36546      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36547      * @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)
36548      * @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)
36549      * @return {Roo.UpdateManager} The UpdateManager
36550      */
36551     setUrl : function(url, params, loadOnce){
36552         if(this.refreshDelegate){
36553             this.removeListener("activate", this.refreshDelegate);
36554         }
36555         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36556         this.on("activate", this.refreshDelegate);
36557         return this.el.getUpdateManager();
36558     },
36559     
36560     _handleRefresh : function(url, params, loadOnce){
36561         if(!loadOnce || !this.loaded){
36562             var updater = this.el.getUpdateManager();
36563             updater.update(url, params, this._setLoaded.createDelegate(this));
36564         }
36565     },
36566     
36567     _setLoaded : function(){
36568         this.loaded = true;
36569     }, 
36570     
36571     /**
36572      * Returns this panel's id
36573      * @return {String} 
36574      */
36575     getId : function(){
36576         return this.el.id;
36577     },
36578     
36579     /** 
36580      * Returns this panel's element - used by regiosn to add.
36581      * @return {Roo.Element} 
36582      */
36583     getEl : function(){
36584         return this.wrapEl || this.el;
36585     },
36586     
36587     adjustForComponents : function(width, height)
36588     {
36589         //Roo.log('adjustForComponents ');
36590         if(this.resizeEl != this.el){
36591             width -= this.el.getFrameWidth('lr');
36592             height -= this.el.getFrameWidth('tb');
36593         }
36594         if(this.toolbar){
36595             var te = this.toolbar.getEl();
36596             height -= te.getHeight();
36597             te.setWidth(width);
36598         }
36599         if(this.footer){
36600             var te = this.footer.getEl();
36601             //Roo.log("footer:" + te.getHeight());
36602             
36603             height -= te.getHeight();
36604             te.setWidth(width);
36605         }
36606         
36607         
36608         if(this.adjustments){
36609             width += this.adjustments[0];
36610             height += this.adjustments[1];
36611         }
36612         return {"width": width, "height": height};
36613     },
36614     
36615     setSize : function(width, height){
36616         if(this.fitToFrame && !this.ignoreResize(width, height)){
36617             if(this.fitContainer && this.resizeEl != this.el){
36618                 this.el.setSize(width, height);
36619             }
36620             var size = this.adjustForComponents(width, height);
36621             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36622             this.fireEvent('resize', this, size.width, size.height);
36623         }
36624     },
36625     
36626     /**
36627      * Returns this panel's title
36628      * @return {String} 
36629      */
36630     getTitle : function(){
36631         return this.title;
36632     },
36633     
36634     /**
36635      * Set this panel's title
36636      * @param {String} title
36637      */
36638     setTitle : function(title){
36639         this.title = title;
36640         if(this.region){
36641             this.region.updatePanelTitle(this, title);
36642         }
36643     },
36644     
36645     /**
36646      * Returns true is this panel was configured to be closable
36647      * @return {Boolean} 
36648      */
36649     isClosable : function(){
36650         return this.closable;
36651     },
36652     
36653     beforeSlide : function(){
36654         this.el.clip();
36655         this.resizeEl.clip();
36656     },
36657     
36658     afterSlide : function(){
36659         this.el.unclip();
36660         this.resizeEl.unclip();
36661     },
36662     
36663     /**
36664      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36665      *   Will fail silently if the {@link #setUrl} method has not been called.
36666      *   This does not activate the panel, just updates its content.
36667      */
36668     refresh : function(){
36669         if(this.refreshDelegate){
36670            this.loaded = false;
36671            this.refreshDelegate();
36672         }
36673     },
36674     
36675     /**
36676      * Destroys this panel
36677      */
36678     destroy : function(){
36679         this.el.removeAllListeners();
36680         var tempEl = document.createElement("span");
36681         tempEl.appendChild(this.el.dom);
36682         tempEl.innerHTML = "";
36683         this.el.remove();
36684         this.el = null;
36685     },
36686     
36687     /**
36688      * form - if the content panel contains a form - this is a reference to it.
36689      * @type {Roo.form.Form}
36690      */
36691     form : false,
36692     /**
36693      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36694      *    This contains a reference to it.
36695      * @type {Roo.View}
36696      */
36697     view : false,
36698     
36699       /**
36700      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36701      * <pre><code>
36702
36703 layout.addxtype({
36704        xtype : 'Form',
36705        items: [ .... ]
36706    }
36707 );
36708
36709 </code></pre>
36710      * @param {Object} cfg Xtype definition of item to add.
36711      */
36712     
36713     addxtype : function(cfg) {
36714         if(cfg.xtype.match(/^UploadCropbox$/)) {
36715
36716             this.cropbox = new Roo.factory(cfg);
36717
36718             this.cropbox.render(this.el);
36719
36720             return this.cropbox;
36721         }
36722         // add form..
36723         if (cfg.xtype.match(/^Form$/)) {
36724             
36725             var el;
36726             //if (this.footer) {
36727             //    el = this.footer.container.insertSibling(false, 'before');
36728             //} else {
36729                 el = this.el.createChild();
36730             //}
36731
36732             this.form = new  Roo.form.Form(cfg);
36733             
36734             
36735             if ( this.form.allItems.length) {
36736                 this.form.render(el.dom);
36737             }
36738             return this.form;
36739         }
36740         // should only have one of theses..
36741         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36742             // views.. should not be just added - used named prop 'view''
36743             
36744             cfg.el = this.el.appendChild(document.createElement("div"));
36745             // factory?
36746             
36747             var ret = new Roo.factory(cfg);
36748              
36749              ret.render && ret.render(false, ''); // render blank..
36750             this.view = ret;
36751             return ret;
36752         }
36753         return false;
36754     }
36755 });
36756
36757
36758
36759
36760
36761
36762
36763
36764
36765
36766
36767
36768 /**
36769  * @class Roo.GridPanel
36770  * @extends Roo.ContentPanel
36771  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36772  * @constructor
36773  * Create a new GridPanel.
36774  * @cfg {Roo.grid.Grid} grid The grid for this panel
36775  */
36776 Roo.GridPanel = function(grid, config){
36777     
36778     // universal ctor...
36779     if (typeof(grid.grid) != 'undefined') {
36780         config = grid;
36781         grid = config.grid;
36782     }
36783     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36784         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36785         
36786     this.wrapper.dom.appendChild(grid.getGridEl().dom);
36787     
36788     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36789     
36790     if(this.toolbar){
36791         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36792     }
36793     // xtype created footer. - not sure if will work as we normally have to render first..
36794     if (this.footer && !this.footer.el && this.footer.xtype) {
36795         
36796         this.footer.container = this.grid.getView().getFooterPanel(true);
36797         this.footer.dataSource = this.grid.dataSource;
36798         this.footer = Roo.factory(this.footer, Roo);
36799         
36800     }
36801     
36802     grid.monitorWindowResize = false; // turn off autosizing
36803     grid.autoHeight = false;
36804     grid.autoWidth = false;
36805     this.grid = grid;
36806     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36807 };
36808
36809 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36810     getId : function(){
36811         return this.grid.id;
36812     },
36813     
36814     /**
36815      * Returns the grid for this panel
36816      * @return {Roo.grid.Grid} 
36817      */
36818     getGrid : function(){
36819         return this.grid;    
36820     },
36821     
36822     setSize : function(width, height){
36823         if(!this.ignoreResize(width, height)){
36824             var grid = this.grid;
36825             var size = this.adjustForComponents(width, height);
36826             grid.getGridEl().setSize(size.width, size.height);
36827             grid.autoSize();
36828         }
36829     },
36830     
36831     beforeSlide : function(){
36832         this.grid.getView().scroller.clip();
36833     },
36834     
36835     afterSlide : function(){
36836         this.grid.getView().scroller.unclip();
36837     },
36838     
36839     destroy : function(){
36840         this.grid.destroy();
36841         delete this.grid;
36842         Roo.GridPanel.superclass.destroy.call(this); 
36843     }
36844 });
36845
36846
36847 /**
36848  * @class Roo.NestedLayoutPanel
36849  * @extends Roo.ContentPanel
36850  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36851  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
36852  *
36853  * 
36854  * @constructor
36855  * Create a new NestedLayoutPanel.
36856  * 
36857  * 
36858  * @param {Roo.BorderLayout} layout [required] The layout for this panel
36859  * @param {String/Object} config A string to set only the title or a config object
36860  */
36861 Roo.NestedLayoutPanel = function(layout, config)
36862 {
36863     // construct with only one argument..
36864     /* FIXME - implement nicer consturctors
36865     if (layout.layout) {
36866         config = layout;
36867         layout = config.layout;
36868         delete config.layout;
36869     }
36870     if (layout.xtype && !layout.getEl) {
36871         // then layout needs constructing..
36872         layout = Roo.factory(layout, Roo);
36873     }
36874     */
36875     
36876     
36877     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36878     
36879     layout.monitorWindowResize = false; // turn off autosizing
36880     this.layout = layout;
36881     this.layout.getEl().addClass("x-layout-nested-layout");
36882     
36883     
36884     
36885     
36886 };
36887
36888 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36889
36890     layout : false,
36891
36892     setSize : function(width, height){
36893         if(!this.ignoreResize(width, height)){
36894             var size = this.adjustForComponents(width, height);
36895             var el = this.layout.getEl();
36896             el.setSize(size.width, size.height);
36897             var touch = el.dom.offsetWidth;
36898             this.layout.layout();
36899             // ie requires a double layout on the first pass
36900             if(Roo.isIE && !this.initialized){
36901                 this.initialized = true;
36902                 this.layout.layout();
36903             }
36904         }
36905     },
36906     
36907     // activate all subpanels if not currently active..
36908     
36909     setActiveState : function(active){
36910         this.active = active;
36911         if(!active){
36912             this.fireEvent("deactivate", this);
36913             return;
36914         }
36915         
36916         this.fireEvent("activate", this);
36917         // not sure if this should happen before or after..
36918         if (!this.layout) {
36919             return; // should not happen..
36920         }
36921         var reg = false;
36922         for (var r in this.layout.regions) {
36923             reg = this.layout.getRegion(r);
36924             if (reg.getActivePanel()) {
36925                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36926                 reg.setActivePanel(reg.getActivePanel());
36927                 continue;
36928             }
36929             if (!reg.panels.length) {
36930                 continue;
36931             }
36932             reg.showPanel(reg.getPanel(0));
36933         }
36934         
36935         
36936         
36937         
36938     },
36939     
36940     /**
36941      * Returns the nested BorderLayout for this panel
36942      * @return {Roo.BorderLayout}
36943      */
36944     getLayout : function(){
36945         return this.layout;
36946     },
36947     
36948      /**
36949      * Adds a xtype elements to the layout of the nested panel
36950      * <pre><code>
36951
36952 panel.addxtype({
36953        xtype : 'ContentPanel',
36954        region: 'west',
36955        items: [ .... ]
36956    }
36957 );
36958
36959 panel.addxtype({
36960         xtype : 'NestedLayoutPanel',
36961         region: 'west',
36962         layout: {
36963            center: { },
36964            west: { }   
36965         },
36966         items : [ ... list of content panels or nested layout panels.. ]
36967    }
36968 );
36969 </code></pre>
36970      * @param {Object} cfg Xtype definition of item to add.
36971      */
36972     addxtype : function(cfg) {
36973         return this.layout.addxtype(cfg);
36974     
36975     }
36976 });
36977
36978 Roo.ScrollPanel = function(el, config, content){
36979     config = config || {};
36980     config.fitToFrame = true;
36981     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36982     
36983     this.el.dom.style.overflow = "hidden";
36984     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36985     this.el.removeClass("x-layout-inactive-content");
36986     this.el.on("mousewheel", this.onWheel, this);
36987
36988     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
36989     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
36990     up.unselectable(); down.unselectable();
36991     up.on("click", this.scrollUp, this);
36992     down.on("click", this.scrollDown, this);
36993     up.addClassOnOver("x-scroller-btn-over");
36994     down.addClassOnOver("x-scroller-btn-over");
36995     up.addClassOnClick("x-scroller-btn-click");
36996     down.addClassOnClick("x-scroller-btn-click");
36997     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36998
36999     this.resizeEl = this.el;
37000     this.el = wrap; this.up = up; this.down = down;
37001 };
37002
37003 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
37004     increment : 100,
37005     wheelIncrement : 5,
37006     scrollUp : function(){
37007         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
37008     },
37009
37010     scrollDown : function(){
37011         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
37012     },
37013
37014     afterScroll : function(){
37015         var el = this.resizeEl;
37016         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
37017         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
37018         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
37019     },
37020
37021     setSize : function(){
37022         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
37023         this.afterScroll();
37024     },
37025
37026     onWheel : function(e){
37027         var d = e.getWheelDelta();
37028         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
37029         this.afterScroll();
37030         e.stopEvent();
37031     },
37032
37033     setContent : function(content, loadScripts){
37034         this.resizeEl.update(content, loadScripts);
37035     }
37036
37037 });
37038
37039
37040
37041 /**
37042  * @class Roo.TreePanel
37043  * @extends Roo.ContentPanel
37044  * @parent Roo.BorderLayout Roo.LayoutDialog builder
37045  * Treepanel component
37046  * 
37047  * @constructor
37048  * Create a new TreePanel. - defaults to fit/scoll contents.
37049  * @param {String/Object} config A string to set only the panel's title, or a config object
37050  */
37051 Roo.TreePanel = function(config){
37052     var el = config.el;
37053     var tree = config.tree;
37054     delete config.tree; 
37055     delete config.el; // hopefull!
37056     
37057     // wrapper for IE7 strict & safari scroll issue
37058     
37059     var treeEl = el.createChild();
37060     config.resizeEl = treeEl;
37061     
37062     
37063     
37064     Roo.TreePanel.superclass.constructor.call(this, el, config);
37065  
37066  
37067     this.tree = new Roo.tree.TreePanel(treeEl , tree);
37068     //console.log(tree);
37069     this.on('activate', function()
37070     {
37071         if (this.tree.rendered) {
37072             return;
37073         }
37074         //console.log('render tree');
37075         this.tree.render();
37076     });
37077     // this should not be needed.. - it's actually the 'el' that resizes?
37078     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
37079     
37080     //this.on('resize',  function (cp, w, h) {
37081     //        this.tree.innerCt.setWidth(w);
37082     //        this.tree.innerCt.setHeight(h);
37083     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
37084     //});
37085
37086         
37087     
37088 };
37089
37090 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
37091     fitToFrame : true,
37092     autoScroll : true,
37093     /*
37094      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
37095      */
37096     tree : false
37097
37098 });
37099 /*
37100  * Based on:
37101  * Ext JS Library 1.1.1
37102  * Copyright(c) 2006-2007, Ext JS, LLC.
37103  *
37104  * Originally Released Under LGPL - original licence link has changed is not relivant.
37105  *
37106  * Fork - LGPL
37107  * <script type="text/javascript">
37108  */
37109  
37110
37111 /**
37112  * @class Roo.ReaderLayout
37113  * @extends Roo.BorderLayout
37114  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
37115  * center region containing two nested regions (a top one for a list view and one for item preview below),
37116  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
37117  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
37118  * expedites the setup of the overall layout and regions for this common application style.
37119  * Example:
37120  <pre><code>
37121 var reader = new Roo.ReaderLayout();
37122 var CP = Roo.ContentPanel;  // shortcut for adding
37123
37124 reader.beginUpdate();
37125 reader.add("north", new CP("north", "North"));
37126 reader.add("west", new CP("west", {title: "West"}));
37127 reader.add("east", new CP("east", {title: "East"}));
37128
37129 reader.regions.listView.add(new CP("listView", "List"));
37130 reader.regions.preview.add(new CP("preview", "Preview"));
37131 reader.endUpdate();
37132 </code></pre>
37133 * @constructor
37134 * Create a new ReaderLayout
37135 * @param {Object} config Configuration options
37136 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
37137 * document.body if omitted)
37138 */
37139 Roo.ReaderLayout = function(config, renderTo){
37140     var c = config || {size:{}};
37141     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
37142         north: c.north !== false ? Roo.apply({
37143             split:false,
37144             initialSize: 32,
37145             titlebar: false
37146         }, c.north) : false,
37147         west: c.west !== false ? Roo.apply({
37148             split:true,
37149             initialSize: 200,
37150             minSize: 175,
37151             maxSize: 400,
37152             titlebar: true,
37153             collapsible: true,
37154             animate: true,
37155             margins:{left:5,right:0,bottom:5,top:5},
37156             cmargins:{left:5,right:5,bottom:5,top:5}
37157         }, c.west) : false,
37158         east: c.east !== false ? Roo.apply({
37159             split:true,
37160             initialSize: 200,
37161             minSize: 175,
37162             maxSize: 400,
37163             titlebar: true,
37164             collapsible: true,
37165             animate: true,
37166             margins:{left:0,right:5,bottom:5,top:5},
37167             cmargins:{left:5,right:5,bottom:5,top:5}
37168         }, c.east) : false,
37169         center: Roo.apply({
37170             tabPosition: 'top',
37171             autoScroll:false,
37172             closeOnTab: true,
37173             titlebar:false,
37174             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
37175         }, c.center)
37176     });
37177
37178     this.el.addClass('x-reader');
37179
37180     this.beginUpdate();
37181
37182     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
37183         south: c.preview !== false ? Roo.apply({
37184             split:true,
37185             initialSize: 200,
37186             minSize: 100,
37187             autoScroll:true,
37188             collapsible:true,
37189             titlebar: true,
37190             cmargins:{top:5,left:0, right:0, bottom:0}
37191         }, c.preview) : false,
37192         center: Roo.apply({
37193             autoScroll:false,
37194             titlebar:false,
37195             minHeight:200
37196         }, c.listView)
37197     });
37198     this.add('center', new Roo.NestedLayoutPanel(inner,
37199             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
37200
37201     this.endUpdate();
37202
37203     this.regions.preview = inner.getRegion('south');
37204     this.regions.listView = inner.getRegion('center');
37205 };
37206
37207 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
37208  * Based on:
37209  * Ext JS Library 1.1.1
37210  * Copyright(c) 2006-2007, Ext JS, LLC.
37211  *
37212  * Originally Released Under LGPL - original licence link has changed is not relivant.
37213  *
37214  * Fork - LGPL
37215  * <script type="text/javascript">
37216  */
37217  
37218 /**
37219  * @class Roo.grid.Grid
37220  * @extends Roo.util.Observable
37221  * This class represents the primary interface of a component based grid control.
37222  * <br><br>Usage:<pre><code>
37223  var grid = new Roo.grid.Grid("my-container-id", {
37224      ds: myDataStore,
37225      cm: myColModel,
37226      selModel: mySelectionModel,
37227      autoSizeColumns: true,
37228      monitorWindowResize: false,
37229      trackMouseOver: true
37230  });
37231  // set any options
37232  grid.render();
37233  * </code></pre>
37234  * <b>Common Problems:</b><br/>
37235  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
37236  * element will correct this<br/>
37237  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
37238  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
37239  * are unpredictable.<br/>
37240  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
37241  * grid to calculate dimensions/offsets.<br/>
37242   * @constructor
37243  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37244  * The container MUST have some type of size defined for the grid to fill. The container will be
37245  * automatically set to position relative if it isn't already.
37246  * @param {Object} config A config object that sets properties on this grid.
37247  */
37248 Roo.grid.Grid = function(container, config){
37249         // initialize the container
37250         this.container = Roo.get(container);
37251         this.container.update("");
37252         this.container.setStyle("overflow", "hidden");
37253     this.container.addClass('x-grid-container');
37254
37255     this.id = this.container.id;
37256
37257     Roo.apply(this, config);
37258     // check and correct shorthanded configs
37259     if(this.ds){
37260         this.dataSource = this.ds;
37261         delete this.ds;
37262     }
37263     if(this.cm){
37264         this.colModel = this.cm;
37265         delete this.cm;
37266     }
37267     if(this.sm){
37268         this.selModel = this.sm;
37269         delete this.sm;
37270     }
37271
37272     if (this.selModel) {
37273         this.selModel = Roo.factory(this.selModel, Roo.grid);
37274         this.sm = this.selModel;
37275         this.sm.xmodule = this.xmodule || false;
37276     }
37277     if (typeof(this.colModel.config) == 'undefined') {
37278         this.colModel = new Roo.grid.ColumnModel(this.colModel);
37279         this.cm = this.colModel;
37280         this.cm.xmodule = this.xmodule || false;
37281     }
37282     if (this.dataSource) {
37283         this.dataSource= Roo.factory(this.dataSource, Roo.data);
37284         this.ds = this.dataSource;
37285         this.ds.xmodule = this.xmodule || false;
37286          
37287     }
37288     
37289     
37290     
37291     if(this.width){
37292         this.container.setWidth(this.width);
37293     }
37294
37295     if(this.height){
37296         this.container.setHeight(this.height);
37297     }
37298     /** @private */
37299         this.addEvents({
37300         // raw events
37301         /**
37302          * @event click
37303          * The raw click event for the entire grid.
37304          * @param {Roo.EventObject} e
37305          */
37306         "click" : true,
37307         /**
37308          * @event dblclick
37309          * The raw dblclick event for the entire grid.
37310          * @param {Roo.EventObject} e
37311          */
37312         "dblclick" : true,
37313         /**
37314          * @event contextmenu
37315          * The raw contextmenu event for the entire grid.
37316          * @param {Roo.EventObject} e
37317          */
37318         "contextmenu" : true,
37319         /**
37320          * @event mousedown
37321          * The raw mousedown event for the entire grid.
37322          * @param {Roo.EventObject} e
37323          */
37324         "mousedown" : true,
37325         /**
37326          * @event mouseup
37327          * The raw mouseup event for the entire grid.
37328          * @param {Roo.EventObject} e
37329          */
37330         "mouseup" : true,
37331         /**
37332          * @event mouseover
37333          * The raw mouseover event for the entire grid.
37334          * @param {Roo.EventObject} e
37335          */
37336         "mouseover" : true,
37337         /**
37338          * @event mouseout
37339          * The raw mouseout event for the entire grid.
37340          * @param {Roo.EventObject} e
37341          */
37342         "mouseout" : true,
37343         /**
37344          * @event keypress
37345          * The raw keypress event for the entire grid.
37346          * @param {Roo.EventObject} e
37347          */
37348         "keypress" : true,
37349         /**
37350          * @event keydown
37351          * The raw keydown event for the entire grid.
37352          * @param {Roo.EventObject} e
37353          */
37354         "keydown" : true,
37355
37356         // custom events
37357
37358         /**
37359          * @event cellclick
37360          * Fires when a cell is clicked
37361          * @param {Grid} this
37362          * @param {Number} rowIndex
37363          * @param {Number} columnIndex
37364          * @param {Roo.EventObject} e
37365          */
37366         "cellclick" : true,
37367         /**
37368          * @event celldblclick
37369          * Fires when a cell is double clicked
37370          * @param {Grid} this
37371          * @param {Number} rowIndex
37372          * @param {Number} columnIndex
37373          * @param {Roo.EventObject} e
37374          */
37375         "celldblclick" : true,
37376         /**
37377          * @event rowclick
37378          * Fires when a row is clicked
37379          * @param {Grid} this
37380          * @param {Number} rowIndex
37381          * @param {Roo.EventObject} e
37382          */
37383         "rowclick" : true,
37384         /**
37385          * @event rowdblclick
37386          * Fires when a row is double clicked
37387          * @param {Grid} this
37388          * @param {Number} rowIndex
37389          * @param {Roo.EventObject} e
37390          */
37391         "rowdblclick" : true,
37392         /**
37393          * @event headerclick
37394          * Fires when a header is clicked
37395          * @param {Grid} this
37396          * @param {Number} columnIndex
37397          * @param {Roo.EventObject} e
37398          */
37399         "headerclick" : true,
37400         /**
37401          * @event headerdblclick
37402          * Fires when a header cell is double clicked
37403          * @param {Grid} this
37404          * @param {Number} columnIndex
37405          * @param {Roo.EventObject} e
37406          */
37407         "headerdblclick" : true,
37408         /**
37409          * @event rowcontextmenu
37410          * Fires when a row is right clicked
37411          * @param {Grid} this
37412          * @param {Number} rowIndex
37413          * @param {Roo.EventObject} e
37414          */
37415         "rowcontextmenu" : true,
37416         /**
37417          * @event cellcontextmenu
37418          * Fires when a cell is right clicked
37419          * @param {Grid} this
37420          * @param {Number} rowIndex
37421          * @param {Number} cellIndex
37422          * @param {Roo.EventObject} e
37423          */
37424          "cellcontextmenu" : true,
37425         /**
37426          * @event headercontextmenu
37427          * Fires when a header is right clicked
37428          * @param {Grid} this
37429          * @param {Number} columnIndex
37430          * @param {Roo.EventObject} e
37431          */
37432         "headercontextmenu" : true,
37433         /**
37434          * @event bodyscroll
37435          * Fires when the body element is scrolled
37436          * @param {Number} scrollLeft
37437          * @param {Number} scrollTop
37438          */
37439         "bodyscroll" : true,
37440         /**
37441          * @event columnresize
37442          * Fires when the user resizes a column
37443          * @param {Number} columnIndex
37444          * @param {Number} newSize
37445          */
37446         "columnresize" : true,
37447         /**
37448          * @event columnmove
37449          * Fires when the user moves a column
37450          * @param {Number} oldIndex
37451          * @param {Number} newIndex
37452          */
37453         "columnmove" : true,
37454         /**
37455          * @event startdrag
37456          * Fires when row(s) start being dragged
37457          * @param {Grid} this
37458          * @param {Roo.GridDD} dd The drag drop object
37459          * @param {event} e The raw browser event
37460          */
37461         "startdrag" : true,
37462         /**
37463          * @event enddrag
37464          * Fires when a drag operation is complete
37465          * @param {Grid} this
37466          * @param {Roo.GridDD} dd The drag drop object
37467          * @param {event} e The raw browser event
37468          */
37469         "enddrag" : true,
37470         /**
37471          * @event dragdrop
37472          * Fires when dragged row(s) are dropped on a valid DD target
37473          * @param {Grid} this
37474          * @param {Roo.GridDD} dd The drag drop object
37475          * @param {String} targetId The target drag drop object
37476          * @param {event} e The raw browser event
37477          */
37478         "dragdrop" : true,
37479         /**
37480          * @event dragover
37481          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37482          * @param {Grid} this
37483          * @param {Roo.GridDD} dd The drag drop object
37484          * @param {String} targetId The target drag drop object
37485          * @param {event} e The raw browser event
37486          */
37487         "dragover" : true,
37488         /**
37489          * @event dragenter
37490          *  Fires when the dragged row(s) first cross another DD target while being dragged
37491          * @param {Grid} this
37492          * @param {Roo.GridDD} dd The drag drop object
37493          * @param {String} targetId The target drag drop object
37494          * @param {event} e The raw browser event
37495          */
37496         "dragenter" : true,
37497         /**
37498          * @event dragout
37499          * Fires when the dragged row(s) leave another DD target while being dragged
37500          * @param {Grid} this
37501          * @param {Roo.GridDD} dd The drag drop object
37502          * @param {String} targetId The target drag drop object
37503          * @param {event} e The raw browser event
37504          */
37505         "dragout" : true,
37506         /**
37507          * @event rowclass
37508          * Fires when a row is rendered, so you can change add a style to it.
37509          * @param {GridView} gridview   The grid view
37510          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37511          */
37512         'rowclass' : true,
37513
37514         /**
37515          * @event render
37516          * Fires when the grid is rendered
37517          * @param {Grid} grid
37518          */
37519         'render' : true
37520     });
37521
37522     Roo.grid.Grid.superclass.constructor.call(this);
37523 };
37524 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
37525     
37526     /**
37527          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
37528          */
37529         /**
37530          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
37531          */
37532         /**
37533          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
37534          */
37535         /**
37536          * @cfg {Roo.data.Store} ds The data store for the grid
37537          */
37538         /**
37539          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
37540          */
37541          
37542          /**
37543          * @cfg {Roo.PagingToolbar} footer the paging toolbar
37544          */
37545         
37546         /**
37547      * @cfg {String} ddGroup - drag drop group.
37548      */
37549       /**
37550      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
37551      */
37552
37553     /**
37554      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
37555      */
37556     minColumnWidth : 25,
37557
37558     /**
37559      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
37560      * <b>on initial render.</b> It is more efficient to explicitly size the columns
37561      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
37562      */
37563     autoSizeColumns : false,
37564
37565     /**
37566      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
37567      */
37568     autoSizeHeaders : true,
37569
37570     /**
37571      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
37572      */
37573     monitorWindowResize : true,
37574
37575     /**
37576      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
37577      * rows measured to get a columns size. Default is 0 (all rows).
37578      */
37579     maxRowsToMeasure : 0,
37580
37581     /**
37582      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
37583      */
37584     trackMouseOver : true,
37585
37586     /**
37587     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
37588     */
37589       /**
37590     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
37591     */
37592     
37593     /**
37594     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
37595     */
37596     enableDragDrop : false,
37597     
37598     /**
37599     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
37600     */
37601     enableColumnMove : true,
37602     
37603     /**
37604     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
37605     */
37606     enableColumnHide : true,
37607     
37608     /**
37609     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
37610     */
37611     enableRowHeightSync : false,
37612     
37613     /**
37614     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
37615     */
37616     stripeRows : true,
37617     
37618     /**
37619     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
37620     */
37621     autoHeight : false,
37622
37623     /**
37624      * @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.
37625      */
37626     autoExpandColumn : false,
37627
37628     /**
37629     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
37630     * Default is 50.
37631     */
37632     autoExpandMin : 50,
37633
37634     /**
37635     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
37636     */
37637     autoExpandMax : 1000,
37638
37639     /**
37640     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
37641     */
37642     view : null,
37643
37644     /**
37645     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
37646     */
37647     loadMask : false,
37648     /**
37649     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
37650     */
37651     dropTarget: false,
37652      /**
37653     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
37654     */ 
37655     sortColMenu : false,
37656     
37657     // private
37658     rendered : false,
37659
37660     /**
37661     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37662     * of a fixed width. Default is false.
37663     */
37664     /**
37665     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37666     */
37667     
37668     
37669     /**
37670     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37671     * %0 is replaced with the number of selected rows.
37672     */
37673     ddText : "{0} selected row{1}",
37674     
37675     
37676     /**
37677      * Called once after all setup has been completed and the grid is ready to be rendered.
37678      * @return {Roo.grid.Grid} this
37679      */
37680     render : function()
37681     {
37682         var c = this.container;
37683         // try to detect autoHeight/width mode
37684         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37685             this.autoHeight = true;
37686         }
37687         var view = this.getView();
37688         view.init(this);
37689
37690         c.on("click", this.onClick, this);
37691         c.on("dblclick", this.onDblClick, this);
37692         c.on("contextmenu", this.onContextMenu, this);
37693         c.on("keydown", this.onKeyDown, this);
37694         if (Roo.isTouch) {
37695             c.on("touchstart", this.onTouchStart, this);
37696         }
37697
37698         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37699
37700         this.getSelectionModel().init(this);
37701
37702         view.render();
37703
37704         if(this.loadMask){
37705             this.loadMask = new Roo.LoadMask(this.container,
37706                     Roo.apply({store:this.dataSource}, this.loadMask));
37707         }
37708         
37709         
37710         if (this.toolbar && this.toolbar.xtype) {
37711             this.toolbar.container = this.getView().getHeaderPanel(true);
37712             this.toolbar = new Roo.Toolbar(this.toolbar);
37713         }
37714         if (this.footer && this.footer.xtype) {
37715             this.footer.dataSource = this.getDataSource();
37716             this.footer.container = this.getView().getFooterPanel(true);
37717             this.footer = Roo.factory(this.footer, Roo);
37718         }
37719         if (this.dropTarget && this.dropTarget.xtype) {
37720             delete this.dropTarget.xtype;
37721             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37722         }
37723         
37724         
37725         this.rendered = true;
37726         this.fireEvent('render', this);
37727         return this;
37728     },
37729
37730     /**
37731      * Reconfigures the grid to use a different Store and Column Model.
37732      * The View will be bound to the new objects and refreshed.
37733      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37734      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37735      */
37736     reconfigure : function(dataSource, colModel){
37737         if(this.loadMask){
37738             this.loadMask.destroy();
37739             this.loadMask = new Roo.LoadMask(this.container,
37740                     Roo.apply({store:dataSource}, this.loadMask));
37741         }
37742         this.view.bind(dataSource, colModel);
37743         this.dataSource = dataSource;
37744         this.colModel = colModel;
37745         this.view.refresh(true);
37746     },
37747     /**
37748      * addColumns
37749      * Add's a column, default at the end..
37750      
37751      * @param {int} position to add (default end)
37752      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
37753      */
37754     addColumns : function(pos, ar)
37755     {
37756         
37757         for (var i =0;i< ar.length;i++) {
37758             var cfg = ar[i];
37759             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37760             this.cm.lookup[cfg.id] = cfg;
37761         }
37762         
37763         
37764         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37765             pos = this.cm.config.length; //this.cm.config.push(cfg);
37766         } 
37767         pos = Math.max(0,pos);
37768         ar.unshift(0);
37769         ar.unshift(pos);
37770         this.cm.config.splice.apply(this.cm.config, ar);
37771         
37772         
37773         
37774         this.view.generateRules(this.cm);
37775         this.view.refresh(true);
37776         
37777     },
37778     
37779     
37780     
37781     
37782     // private
37783     onKeyDown : function(e){
37784         this.fireEvent("keydown", e);
37785     },
37786
37787     /**
37788      * Destroy this grid.
37789      * @param {Boolean} removeEl True to remove the element
37790      */
37791     destroy : function(removeEl, keepListeners){
37792         if(this.loadMask){
37793             this.loadMask.destroy();
37794         }
37795         var c = this.container;
37796         c.removeAllListeners();
37797         this.view.destroy();
37798         this.colModel.purgeListeners();
37799         if(!keepListeners){
37800             this.purgeListeners();
37801         }
37802         c.update("");
37803         if(removeEl === true){
37804             c.remove();
37805         }
37806     },
37807
37808     // private
37809     processEvent : function(name, e){
37810         // does this fire select???
37811         //Roo.log('grid:processEvent '  + name);
37812         
37813         if (name != 'touchstart' ) {
37814             this.fireEvent(name, e);    
37815         }
37816         
37817         var t = e.getTarget();
37818         var v = this.view;
37819         var header = v.findHeaderIndex(t);
37820         if(header !== false){
37821             var ename = name == 'touchstart' ? 'click' : name;
37822              
37823             this.fireEvent("header" + ename, this, header, e);
37824         }else{
37825             var row = v.findRowIndex(t);
37826             var cell = v.findCellIndex(t);
37827             if (name == 'touchstart') {
37828                 // first touch is always a click.
37829                 // hopefull this happens after selection is updated.?
37830                 name = false;
37831                 
37832                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37833                     var cs = this.selModel.getSelectedCell();
37834                     if (row == cs[0] && cell == cs[1]){
37835                         name = 'dblclick';
37836                     }
37837                 }
37838                 if (typeof(this.selModel.getSelections) != 'undefined') {
37839                     var cs = this.selModel.getSelections();
37840                     var ds = this.dataSource;
37841                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
37842                         name = 'dblclick';
37843                     }
37844                 }
37845                 if (!name) {
37846                     return;
37847                 }
37848             }
37849             
37850             
37851             if(row !== false){
37852                 this.fireEvent("row" + name, this, row, e);
37853                 if(cell !== false){
37854                     this.fireEvent("cell" + name, this, row, cell, e);
37855                 }
37856             }
37857         }
37858     },
37859
37860     // private
37861     onClick : function(e){
37862         this.processEvent("click", e);
37863     },
37864    // private
37865     onTouchStart : function(e){
37866         this.processEvent("touchstart", e);
37867     },
37868
37869     // private
37870     onContextMenu : function(e, t){
37871         this.processEvent("contextmenu", e);
37872     },
37873
37874     // private
37875     onDblClick : function(e){
37876         this.processEvent("dblclick", e);
37877     },
37878
37879     // private
37880     walkCells : function(row, col, step, fn, scope){
37881         var cm = this.colModel, clen = cm.getColumnCount();
37882         var ds = this.dataSource, rlen = ds.getCount(), first = true;
37883         if(step < 0){
37884             if(col < 0){
37885                 row--;
37886                 first = false;
37887             }
37888             while(row >= 0){
37889                 if(!first){
37890                     col = clen-1;
37891                 }
37892                 first = false;
37893                 while(col >= 0){
37894                     if(fn.call(scope || this, row, col, cm) === true){
37895                         return [row, col];
37896                     }
37897                     col--;
37898                 }
37899                 row--;
37900             }
37901         } else {
37902             if(col >= clen){
37903                 row++;
37904                 first = false;
37905             }
37906             while(row < rlen){
37907                 if(!first){
37908                     col = 0;
37909                 }
37910                 first = false;
37911                 while(col < clen){
37912                     if(fn.call(scope || this, row, col, cm) === true){
37913                         return [row, col];
37914                     }
37915                     col++;
37916                 }
37917                 row++;
37918             }
37919         }
37920         return null;
37921     },
37922
37923     // private
37924     getSelections : function(){
37925         return this.selModel.getSelections();
37926     },
37927
37928     /**
37929      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37930      * but if manual update is required this method will initiate it.
37931      */
37932     autoSize : function(){
37933         if(this.rendered){
37934             this.view.layout();
37935             if(this.view.adjustForScroll){
37936                 this.view.adjustForScroll();
37937             }
37938         }
37939     },
37940
37941     /**
37942      * Returns the grid's underlying element.
37943      * @return {Element} The element
37944      */
37945     getGridEl : function(){
37946         return this.container;
37947     },
37948
37949     // private for compatibility, overridden by editor grid
37950     stopEditing : function(){},
37951
37952     /**
37953      * Returns the grid's SelectionModel.
37954      * @return {SelectionModel}
37955      */
37956     getSelectionModel : function(){
37957         if(!this.selModel){
37958             this.selModel = new Roo.grid.RowSelectionModel();
37959         }
37960         return this.selModel;
37961     },
37962
37963     /**
37964      * Returns the grid's DataSource.
37965      * @return {DataSource}
37966      */
37967     getDataSource : function(){
37968         return this.dataSource;
37969     },
37970
37971     /**
37972      * Returns the grid's ColumnModel.
37973      * @return {ColumnModel}
37974      */
37975     getColumnModel : function(){
37976         return this.colModel;
37977     },
37978
37979     /**
37980      * Returns the grid's GridView object.
37981      * @return {GridView}
37982      */
37983     getView : function(){
37984         if(!this.view){
37985             this.view = new Roo.grid.GridView(this.viewConfig);
37986             this.relayEvents(this.view, [
37987                 "beforerowremoved", "beforerowsinserted",
37988                 "beforerefresh", "rowremoved",
37989                 "rowsinserted", "rowupdated" ,"refresh"
37990             ]);
37991         }
37992         return this.view;
37993     },
37994     /**
37995      * Called to get grid's drag proxy text, by default returns this.ddText.
37996      * Override this to put something different in the dragged text.
37997      * @return {String}
37998      */
37999     getDragDropText : function(){
38000         var count = this.selModel.getCount();
38001         return String.format(this.ddText, count, count == 1 ? '' : 's');
38002     }
38003 });
38004 /*
38005  * Based on:
38006  * Ext JS Library 1.1.1
38007  * Copyright(c) 2006-2007, Ext JS, LLC.
38008  *
38009  * Originally Released Under LGPL - original licence link has changed is not relivant.
38010  *
38011  * Fork - LGPL
38012  * <script type="text/javascript">
38013  */
38014  /**
38015  * @class Roo.grid.AbstractGridView
38016  * @extends Roo.util.Observable
38017  * @abstract
38018  * Abstract base class for grid Views
38019  * @constructor
38020  */
38021 Roo.grid.AbstractGridView = function(){
38022         this.grid = null;
38023         
38024         this.events = {
38025             "beforerowremoved" : true,
38026             "beforerowsinserted" : true,
38027             "beforerefresh" : true,
38028             "rowremoved" : true,
38029             "rowsinserted" : true,
38030             "rowupdated" : true,
38031             "refresh" : true
38032         };
38033     Roo.grid.AbstractGridView.superclass.constructor.call(this);
38034 };
38035
38036 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
38037     rowClass : "x-grid-row",
38038     cellClass : "x-grid-cell",
38039     tdClass : "x-grid-td",
38040     hdClass : "x-grid-hd",
38041     splitClass : "x-grid-hd-split",
38042     
38043     init: function(grid){
38044         this.grid = grid;
38045                 var cid = this.grid.getGridEl().id;
38046         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
38047         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
38048         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
38049         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
38050         },
38051         
38052     getColumnRenderers : function(){
38053         var renderers = [];
38054         var cm = this.grid.colModel;
38055         var colCount = cm.getColumnCount();
38056         for(var i = 0; i < colCount; i++){
38057             renderers[i] = cm.getRenderer(i);
38058         }
38059         return renderers;
38060     },
38061     
38062     getColumnIds : function(){
38063         var ids = [];
38064         var cm = this.grid.colModel;
38065         var colCount = cm.getColumnCount();
38066         for(var i = 0; i < colCount; i++){
38067             ids[i] = cm.getColumnId(i);
38068         }
38069         return ids;
38070     },
38071     
38072     getDataIndexes : function(){
38073         if(!this.indexMap){
38074             this.indexMap = this.buildIndexMap();
38075         }
38076         return this.indexMap.colToData;
38077     },
38078     
38079     getColumnIndexByDataIndex : function(dataIndex){
38080         if(!this.indexMap){
38081             this.indexMap = this.buildIndexMap();
38082         }
38083         return this.indexMap.dataToCol[dataIndex];
38084     },
38085     
38086     /**
38087      * Set a css style for a column dynamically. 
38088      * @param {Number} colIndex The index of the column
38089      * @param {String} name The css property name
38090      * @param {String} value The css value
38091      */
38092     setCSSStyle : function(colIndex, name, value){
38093         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
38094         Roo.util.CSS.updateRule(selector, name, value);
38095     },
38096     
38097     generateRules : function(cm){
38098         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
38099         Roo.util.CSS.removeStyleSheet(rulesId);
38100         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38101             var cid = cm.getColumnId(i);
38102             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
38103                          this.tdSelector, cid, " {\n}\n",
38104                          this.hdSelector, cid, " {\n}\n",
38105                          this.splitSelector, cid, " {\n}\n");
38106         }
38107         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38108     }
38109 });/*
38110  * Based on:
38111  * Ext JS Library 1.1.1
38112  * Copyright(c) 2006-2007, Ext JS, LLC.
38113  *
38114  * Originally Released Under LGPL - original licence link has changed is not relivant.
38115  *
38116  * Fork - LGPL
38117  * <script type="text/javascript">
38118  */
38119
38120 // private
38121 // This is a support class used internally by the Grid components
38122 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
38123     this.grid = grid;
38124     this.view = grid.getView();
38125     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
38126     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
38127     if(hd2){
38128         this.setHandleElId(Roo.id(hd));
38129         this.setOuterHandleElId(Roo.id(hd2));
38130     }
38131     this.scroll = false;
38132 };
38133 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
38134     maxDragWidth: 120,
38135     getDragData : function(e){
38136         var t = Roo.lib.Event.getTarget(e);
38137         var h = this.view.findHeaderCell(t);
38138         if(h){
38139             return {ddel: h.firstChild, header:h};
38140         }
38141         return false;
38142     },
38143
38144     onInitDrag : function(e){
38145         this.view.headersDisabled = true;
38146         var clone = this.dragData.ddel.cloneNode(true);
38147         clone.id = Roo.id();
38148         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
38149         this.proxy.update(clone);
38150         return true;
38151     },
38152
38153     afterValidDrop : function(){
38154         var v = this.view;
38155         setTimeout(function(){
38156             v.headersDisabled = false;
38157         }, 50);
38158     },
38159
38160     afterInvalidDrop : function(){
38161         var v = this.view;
38162         setTimeout(function(){
38163             v.headersDisabled = false;
38164         }, 50);
38165     }
38166 });
38167 /*
38168  * Based on:
38169  * Ext JS Library 1.1.1
38170  * Copyright(c) 2006-2007, Ext JS, LLC.
38171  *
38172  * Originally Released Under LGPL - original licence link has changed is not relivant.
38173  *
38174  * Fork - LGPL
38175  * <script type="text/javascript">
38176  */
38177 // private
38178 // This is a support class used internally by the Grid components
38179 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
38180     this.grid = grid;
38181     this.view = grid.getView();
38182     // split the proxies so they don't interfere with mouse events
38183     this.proxyTop = Roo.DomHelper.append(document.body, {
38184         cls:"col-move-top", html:"&#160;"
38185     }, true);
38186     this.proxyBottom = Roo.DomHelper.append(document.body, {
38187         cls:"col-move-bottom", html:"&#160;"
38188     }, true);
38189     this.proxyTop.hide = this.proxyBottom.hide = function(){
38190         this.setLeftTop(-100,-100);
38191         this.setStyle("visibility", "hidden");
38192     };
38193     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
38194     // temporarily disabled
38195     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
38196     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
38197 };
38198 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
38199     proxyOffsets : [-4, -9],
38200     fly: Roo.Element.fly,
38201
38202     getTargetFromEvent : function(e){
38203         var t = Roo.lib.Event.getTarget(e);
38204         var cindex = this.view.findCellIndex(t);
38205         if(cindex !== false){
38206             return this.view.getHeaderCell(cindex);
38207         }
38208         return null;
38209     },
38210
38211     nextVisible : function(h){
38212         var v = this.view, cm = this.grid.colModel;
38213         h = h.nextSibling;
38214         while(h){
38215             if(!cm.isHidden(v.getCellIndex(h))){
38216                 return h;
38217             }
38218             h = h.nextSibling;
38219         }
38220         return null;
38221     },
38222
38223     prevVisible : function(h){
38224         var v = this.view, cm = this.grid.colModel;
38225         h = h.prevSibling;
38226         while(h){
38227             if(!cm.isHidden(v.getCellIndex(h))){
38228                 return h;
38229             }
38230             h = h.prevSibling;
38231         }
38232         return null;
38233     },
38234
38235     positionIndicator : function(h, n, e){
38236         var x = Roo.lib.Event.getPageX(e);
38237         var r = Roo.lib.Dom.getRegion(n.firstChild);
38238         var px, pt, py = r.top + this.proxyOffsets[1];
38239         if((r.right - x) <= (r.right-r.left)/2){
38240             px = r.right+this.view.borderWidth;
38241             pt = "after";
38242         }else{
38243             px = r.left;
38244             pt = "before";
38245         }
38246         var oldIndex = this.view.getCellIndex(h);
38247         var newIndex = this.view.getCellIndex(n);
38248
38249         if(this.grid.colModel.isFixed(newIndex)){
38250             return false;
38251         }
38252
38253         var locked = this.grid.colModel.isLocked(newIndex);
38254
38255         if(pt == "after"){
38256             newIndex++;
38257         }
38258         if(oldIndex < newIndex){
38259             newIndex--;
38260         }
38261         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
38262             return false;
38263         }
38264         px +=  this.proxyOffsets[0];
38265         this.proxyTop.setLeftTop(px, py);
38266         this.proxyTop.show();
38267         if(!this.bottomOffset){
38268             this.bottomOffset = this.view.mainHd.getHeight();
38269         }
38270         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
38271         this.proxyBottom.show();
38272         return pt;
38273     },
38274
38275     onNodeEnter : function(n, dd, e, data){
38276         if(data.header != n){
38277             this.positionIndicator(data.header, n, e);
38278         }
38279     },
38280
38281     onNodeOver : function(n, dd, e, data){
38282         var result = false;
38283         if(data.header != n){
38284             result = this.positionIndicator(data.header, n, e);
38285         }
38286         if(!result){
38287             this.proxyTop.hide();
38288             this.proxyBottom.hide();
38289         }
38290         return result ? this.dropAllowed : this.dropNotAllowed;
38291     },
38292
38293     onNodeOut : function(n, dd, e, data){
38294         this.proxyTop.hide();
38295         this.proxyBottom.hide();
38296     },
38297
38298     onNodeDrop : function(n, dd, e, data){
38299         var h = data.header;
38300         if(h != n){
38301             var cm = this.grid.colModel;
38302             var x = Roo.lib.Event.getPageX(e);
38303             var r = Roo.lib.Dom.getRegion(n.firstChild);
38304             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
38305             var oldIndex = this.view.getCellIndex(h);
38306             var newIndex = this.view.getCellIndex(n);
38307             var locked = cm.isLocked(newIndex);
38308             if(pt == "after"){
38309                 newIndex++;
38310             }
38311             if(oldIndex < newIndex){
38312                 newIndex--;
38313             }
38314             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
38315                 return false;
38316             }
38317             cm.setLocked(oldIndex, locked, true);
38318             cm.moveColumn(oldIndex, newIndex);
38319             this.grid.fireEvent("columnmove", oldIndex, newIndex);
38320             return true;
38321         }
38322         return false;
38323     }
38324 });
38325 /*
38326  * Based on:
38327  * Ext JS Library 1.1.1
38328  * Copyright(c) 2006-2007, Ext JS, LLC.
38329  *
38330  * Originally Released Under LGPL - original licence link has changed is not relivant.
38331  *
38332  * Fork - LGPL
38333  * <script type="text/javascript">
38334  */
38335   
38336 /**
38337  * @class Roo.grid.GridView
38338  * @extends Roo.util.Observable
38339  *
38340  * @constructor
38341  * @param {Object} config
38342  */
38343 Roo.grid.GridView = function(config){
38344     Roo.grid.GridView.superclass.constructor.call(this);
38345     this.el = null;
38346
38347     Roo.apply(this, config);
38348 };
38349
38350 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
38351
38352     unselectable :  'unselectable="on"',
38353     unselectableCls :  'x-unselectable',
38354     
38355     
38356     rowClass : "x-grid-row",
38357
38358     cellClass : "x-grid-col",
38359
38360     tdClass : "x-grid-td",
38361
38362     hdClass : "x-grid-hd",
38363
38364     splitClass : "x-grid-split",
38365
38366     sortClasses : ["sort-asc", "sort-desc"],
38367
38368     enableMoveAnim : false,
38369
38370     hlColor: "C3DAF9",
38371
38372     dh : Roo.DomHelper,
38373
38374     fly : Roo.Element.fly,
38375
38376     css : Roo.util.CSS,
38377
38378     borderWidth: 1,
38379
38380     splitOffset: 3,
38381
38382     scrollIncrement : 22,
38383
38384     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
38385
38386     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
38387
38388     bind : function(ds, cm){
38389         if(this.ds){
38390             this.ds.un("load", this.onLoad, this);
38391             this.ds.un("datachanged", this.onDataChange, this);
38392             this.ds.un("add", this.onAdd, this);
38393             this.ds.un("remove", this.onRemove, this);
38394             this.ds.un("update", this.onUpdate, this);
38395             this.ds.un("clear", this.onClear, this);
38396         }
38397         if(ds){
38398             ds.on("load", this.onLoad, this);
38399             ds.on("datachanged", this.onDataChange, this);
38400             ds.on("add", this.onAdd, this);
38401             ds.on("remove", this.onRemove, this);
38402             ds.on("update", this.onUpdate, this);
38403             ds.on("clear", this.onClear, this);
38404         }
38405         this.ds = ds;
38406
38407         if(this.cm){
38408             this.cm.un("widthchange", this.onColWidthChange, this);
38409             this.cm.un("headerchange", this.onHeaderChange, this);
38410             this.cm.un("hiddenchange", this.onHiddenChange, this);
38411             this.cm.un("columnmoved", this.onColumnMove, this);
38412             this.cm.un("columnlockchange", this.onColumnLock, this);
38413         }
38414         if(cm){
38415             this.generateRules(cm);
38416             cm.on("widthchange", this.onColWidthChange, this);
38417             cm.on("headerchange", this.onHeaderChange, this);
38418             cm.on("hiddenchange", this.onHiddenChange, this);
38419             cm.on("columnmoved", this.onColumnMove, this);
38420             cm.on("columnlockchange", this.onColumnLock, this);
38421         }
38422         this.cm = cm;
38423     },
38424
38425     init: function(grid){
38426         Roo.grid.GridView.superclass.init.call(this, grid);
38427
38428         this.bind(grid.dataSource, grid.colModel);
38429
38430         grid.on("headerclick", this.handleHeaderClick, this);
38431
38432         if(grid.trackMouseOver){
38433             grid.on("mouseover", this.onRowOver, this);
38434             grid.on("mouseout", this.onRowOut, this);
38435         }
38436         grid.cancelTextSelection = function(){};
38437         this.gridId = grid.id;
38438
38439         var tpls = this.templates || {};
38440
38441         if(!tpls.master){
38442             tpls.master = new Roo.Template(
38443                '<div class="x-grid" hidefocus="true">',
38444                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
38445                   '<div class="x-grid-topbar"></div>',
38446                   '<div class="x-grid-scroller"><div></div></div>',
38447                   '<div class="x-grid-locked">',
38448                       '<div class="x-grid-header">{lockedHeader}</div>',
38449                       '<div class="x-grid-body">{lockedBody}</div>',
38450                   "</div>",
38451                   '<div class="x-grid-viewport">',
38452                       '<div class="x-grid-header">{header}</div>',
38453                       '<div class="x-grid-body">{body}</div>',
38454                   "</div>",
38455                   '<div class="x-grid-bottombar"></div>',
38456                  
38457                   '<div class="x-grid-resize-proxy">&#160;</div>',
38458                "</div>"
38459             );
38460             tpls.master.disableformats = true;
38461         }
38462
38463         if(!tpls.header){
38464             tpls.header = new Roo.Template(
38465                '<table border="0" cellspacing="0" cellpadding="0">',
38466                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
38467                "</table>{splits}"
38468             );
38469             tpls.header.disableformats = true;
38470         }
38471         tpls.header.compile();
38472
38473         if(!tpls.hcell){
38474             tpls.hcell = new Roo.Template(
38475                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
38476                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
38477                 "</div></td>"
38478              );
38479              tpls.hcell.disableFormats = true;
38480         }
38481         tpls.hcell.compile();
38482
38483         if(!tpls.hsplit){
38484             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
38485                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
38486             tpls.hsplit.disableFormats = true;
38487         }
38488         tpls.hsplit.compile();
38489
38490         if(!tpls.body){
38491             tpls.body = new Roo.Template(
38492                '<table border="0" cellspacing="0" cellpadding="0">',
38493                "<tbody>{rows}</tbody>",
38494                "</table>"
38495             );
38496             tpls.body.disableFormats = true;
38497         }
38498         tpls.body.compile();
38499
38500         if(!tpls.row){
38501             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
38502             tpls.row.disableFormats = true;
38503         }
38504         tpls.row.compile();
38505
38506         if(!tpls.cell){
38507             tpls.cell = new Roo.Template(
38508                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
38509                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
38510                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
38511                 "</td>"
38512             );
38513             tpls.cell.disableFormats = true;
38514         }
38515         tpls.cell.compile();
38516
38517         this.templates = tpls;
38518     },
38519
38520     // remap these for backwards compat
38521     onColWidthChange : function(){
38522         this.updateColumns.apply(this, arguments);
38523     },
38524     onHeaderChange : function(){
38525         this.updateHeaders.apply(this, arguments);
38526     }, 
38527     onHiddenChange : function(){
38528         this.handleHiddenChange.apply(this, arguments);
38529     },
38530     onColumnMove : function(){
38531         this.handleColumnMove.apply(this, arguments);
38532     },
38533     onColumnLock : function(){
38534         this.handleLockChange.apply(this, arguments);
38535     },
38536
38537     onDataChange : function(){
38538         this.refresh();
38539         this.updateHeaderSortState();
38540     },
38541
38542     onClear : function(){
38543         this.refresh();
38544     },
38545
38546     onUpdate : function(ds, record){
38547         this.refreshRow(record);
38548     },
38549
38550     refreshRow : function(record){
38551         var ds = this.ds, index;
38552         if(typeof record == 'number'){
38553             index = record;
38554             record = ds.getAt(index);
38555         }else{
38556             index = ds.indexOf(record);
38557         }
38558         this.insertRows(ds, index, index, true);
38559         this.onRemove(ds, record, index+1, true);
38560         this.syncRowHeights(index, index);
38561         this.layout();
38562         this.fireEvent("rowupdated", this, index, record);
38563     },
38564
38565     onAdd : function(ds, records, index){
38566         this.insertRows(ds, index, index + (records.length-1));
38567     },
38568
38569     onRemove : function(ds, record, index, isUpdate){
38570         if(isUpdate !== true){
38571             this.fireEvent("beforerowremoved", this, index, record);
38572         }
38573         var bt = this.getBodyTable(), lt = this.getLockedTable();
38574         if(bt.rows[index]){
38575             bt.firstChild.removeChild(bt.rows[index]);
38576         }
38577         if(lt.rows[index]){
38578             lt.firstChild.removeChild(lt.rows[index]);
38579         }
38580         if(isUpdate !== true){
38581             this.stripeRows(index);
38582             this.syncRowHeights(index, index);
38583             this.layout();
38584             this.fireEvent("rowremoved", this, index, record);
38585         }
38586     },
38587
38588     onLoad : function(){
38589         this.scrollToTop();
38590     },
38591
38592     /**
38593      * Scrolls the grid to the top
38594      */
38595     scrollToTop : function(){
38596         if(this.scroller){
38597             this.scroller.dom.scrollTop = 0;
38598             this.syncScroll();
38599         }
38600     },
38601
38602     /**
38603      * Gets a panel in the header of the grid that can be used for toolbars etc.
38604      * After modifying the contents of this panel a call to grid.autoSize() may be
38605      * required to register any changes in size.
38606      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
38607      * @return Roo.Element
38608      */
38609     getHeaderPanel : function(doShow){
38610         if(doShow){
38611             this.headerPanel.show();
38612         }
38613         return this.headerPanel;
38614     },
38615
38616     /**
38617      * Gets a panel in the footer of the grid that can be used for toolbars etc.
38618      * After modifying the contents of this panel a call to grid.autoSize() may be
38619      * required to register any changes in size.
38620      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
38621      * @return Roo.Element
38622      */
38623     getFooterPanel : function(doShow){
38624         if(doShow){
38625             this.footerPanel.show();
38626         }
38627         return this.footerPanel;
38628     },
38629
38630     initElements : function(){
38631         var E = Roo.Element;
38632         var el = this.grid.getGridEl().dom.firstChild;
38633         var cs = el.childNodes;
38634
38635         this.el = new E(el);
38636         
38637          this.focusEl = new E(el.firstChild);
38638         this.focusEl.swallowEvent("click", true);
38639         
38640         this.headerPanel = new E(cs[1]);
38641         this.headerPanel.enableDisplayMode("block");
38642
38643         this.scroller = new E(cs[2]);
38644         this.scrollSizer = new E(this.scroller.dom.firstChild);
38645
38646         this.lockedWrap = new E(cs[3]);
38647         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
38648         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
38649
38650         this.mainWrap = new E(cs[4]);
38651         this.mainHd = new E(this.mainWrap.dom.firstChild);
38652         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
38653
38654         this.footerPanel = new E(cs[5]);
38655         this.footerPanel.enableDisplayMode("block");
38656
38657         this.resizeProxy = new E(cs[6]);
38658
38659         this.headerSelector = String.format(
38660            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38661            this.lockedHd.id, this.mainHd.id
38662         );
38663
38664         this.splitterSelector = String.format(
38665            '#{0} div.x-grid-split, #{1} div.x-grid-split',
38666            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38667         );
38668     },
38669     idToCssName : function(s)
38670     {
38671         return s.replace(/[^a-z0-9]+/ig, '-');
38672     },
38673
38674     getHeaderCell : function(index){
38675         return Roo.DomQuery.select(this.headerSelector)[index];
38676     },
38677
38678     getHeaderCellMeasure : function(index){
38679         return this.getHeaderCell(index).firstChild;
38680     },
38681
38682     getHeaderCellText : function(index){
38683         return this.getHeaderCell(index).firstChild.firstChild;
38684     },
38685
38686     getLockedTable : function(){
38687         return this.lockedBody.dom.firstChild;
38688     },
38689
38690     getBodyTable : function(){
38691         return this.mainBody.dom.firstChild;
38692     },
38693
38694     getLockedRow : function(index){
38695         return this.getLockedTable().rows[index];
38696     },
38697
38698     getRow : function(index){
38699         return this.getBodyTable().rows[index];
38700     },
38701
38702     getRowComposite : function(index){
38703         if(!this.rowEl){
38704             this.rowEl = new Roo.CompositeElementLite();
38705         }
38706         var els = [], lrow, mrow;
38707         if(lrow = this.getLockedRow(index)){
38708             els.push(lrow);
38709         }
38710         if(mrow = this.getRow(index)){
38711             els.push(mrow);
38712         }
38713         this.rowEl.elements = els;
38714         return this.rowEl;
38715     },
38716     /**
38717      * Gets the 'td' of the cell
38718      * 
38719      * @param {Integer} rowIndex row to select
38720      * @param {Integer} colIndex column to select
38721      * 
38722      * @return {Object} 
38723      */
38724     getCell : function(rowIndex, colIndex){
38725         var locked = this.cm.getLockedCount();
38726         var source;
38727         if(colIndex < locked){
38728             source = this.lockedBody.dom.firstChild;
38729         }else{
38730             source = this.mainBody.dom.firstChild;
38731             colIndex -= locked;
38732         }
38733         return source.rows[rowIndex].childNodes[colIndex];
38734     },
38735
38736     getCellText : function(rowIndex, colIndex){
38737         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38738     },
38739
38740     getCellBox : function(cell){
38741         var b = this.fly(cell).getBox();
38742         if(Roo.isOpera){ // opera fails to report the Y
38743             b.y = cell.offsetTop + this.mainBody.getY();
38744         }
38745         return b;
38746     },
38747
38748     getCellIndex : function(cell){
38749         var id = String(cell.className).match(this.cellRE);
38750         if(id){
38751             return parseInt(id[1], 10);
38752         }
38753         return 0;
38754     },
38755
38756     findHeaderIndex : function(n){
38757         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38758         return r ? this.getCellIndex(r) : false;
38759     },
38760
38761     findHeaderCell : function(n){
38762         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38763         return r ? r : false;
38764     },
38765
38766     findRowIndex : function(n){
38767         if(!n){
38768             return false;
38769         }
38770         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38771         return r ? r.rowIndex : false;
38772     },
38773
38774     findCellIndex : function(node){
38775         var stop = this.el.dom;
38776         while(node && node != stop){
38777             if(this.findRE.test(node.className)){
38778                 return this.getCellIndex(node);
38779             }
38780             node = node.parentNode;
38781         }
38782         return false;
38783     },
38784
38785     getColumnId : function(index){
38786         return this.cm.getColumnId(index);
38787     },
38788
38789     getSplitters : function()
38790     {
38791         if(this.splitterSelector){
38792            return Roo.DomQuery.select(this.splitterSelector);
38793         }else{
38794             return null;
38795       }
38796     },
38797
38798     getSplitter : function(index){
38799         return this.getSplitters()[index];
38800     },
38801
38802     onRowOver : function(e, t){
38803         var row;
38804         if((row = this.findRowIndex(t)) !== false){
38805             this.getRowComposite(row).addClass("x-grid-row-over");
38806         }
38807     },
38808
38809     onRowOut : function(e, t){
38810         var row;
38811         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38812             this.getRowComposite(row).removeClass("x-grid-row-over");
38813         }
38814     },
38815
38816     renderHeaders : function(){
38817         var cm = this.cm;
38818         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38819         var cb = [], lb = [], sb = [], lsb = [], p = {};
38820         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38821             p.cellId = "x-grid-hd-0-" + i;
38822             p.splitId = "x-grid-csplit-0-" + i;
38823             p.id = cm.getColumnId(i);
38824             p.value = cm.getColumnHeader(i) || "";
38825             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
38826             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38827             if(!cm.isLocked(i)){
38828                 cb[cb.length] = ct.apply(p);
38829                 sb[sb.length] = st.apply(p);
38830             }else{
38831                 lb[lb.length] = ct.apply(p);
38832                 lsb[lsb.length] = st.apply(p);
38833             }
38834         }
38835         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38836                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38837     },
38838
38839     updateHeaders : function(){
38840         var html = this.renderHeaders();
38841         this.lockedHd.update(html[0]);
38842         this.mainHd.update(html[1]);
38843     },
38844
38845     /**
38846      * Focuses the specified row.
38847      * @param {Number} row The row index
38848      */
38849     focusRow : function(row)
38850     {
38851         //Roo.log('GridView.focusRow');
38852         var x = this.scroller.dom.scrollLeft;
38853         this.focusCell(row, 0, false);
38854         this.scroller.dom.scrollLeft = x;
38855     },
38856
38857     /**
38858      * Focuses the specified cell.
38859      * @param {Number} row The row index
38860      * @param {Number} col The column index
38861      * @param {Boolean} hscroll false to disable horizontal scrolling
38862      */
38863     focusCell : function(row, col, hscroll)
38864     {
38865         //Roo.log('GridView.focusCell');
38866         var el = this.ensureVisible(row, col, hscroll);
38867         this.focusEl.alignTo(el, "tl-tl");
38868         if(Roo.isGecko){
38869             this.focusEl.focus();
38870         }else{
38871             this.focusEl.focus.defer(1, this.focusEl);
38872         }
38873     },
38874
38875     /**
38876      * Scrolls the specified cell into view
38877      * @param {Number} row The row index
38878      * @param {Number} col The column index
38879      * @param {Boolean} hscroll false to disable horizontal scrolling
38880      */
38881     ensureVisible : function(row, col, hscroll)
38882     {
38883         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38884         //return null; //disable for testing.
38885         if(typeof row != "number"){
38886             row = row.rowIndex;
38887         }
38888         if(row < 0 && row >= this.ds.getCount()){
38889             return  null;
38890         }
38891         col = (col !== undefined ? col : 0);
38892         var cm = this.grid.colModel;
38893         while(cm.isHidden(col)){
38894             col++;
38895         }
38896
38897         var el = this.getCell(row, col);
38898         if(!el){
38899             return null;
38900         }
38901         var c = this.scroller.dom;
38902
38903         var ctop = parseInt(el.offsetTop, 10);
38904         var cleft = parseInt(el.offsetLeft, 10);
38905         var cbot = ctop + el.offsetHeight;
38906         var cright = cleft + el.offsetWidth;
38907         
38908         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38909         var stop = parseInt(c.scrollTop, 10);
38910         var sleft = parseInt(c.scrollLeft, 10);
38911         var sbot = stop + ch;
38912         var sright = sleft + c.clientWidth;
38913         /*
38914         Roo.log('GridView.ensureVisible:' +
38915                 ' ctop:' + ctop +
38916                 ' c.clientHeight:' + c.clientHeight +
38917                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38918                 ' stop:' + stop +
38919                 ' cbot:' + cbot +
38920                 ' sbot:' + sbot +
38921                 ' ch:' + ch  
38922                 );
38923         */
38924         if(ctop < stop){
38925             c.scrollTop = ctop;
38926             //Roo.log("set scrolltop to ctop DISABLE?");
38927         }else if(cbot > sbot){
38928             //Roo.log("set scrolltop to cbot-ch");
38929             c.scrollTop = cbot-ch;
38930         }
38931         
38932         if(hscroll !== false){
38933             if(cleft < sleft){
38934                 c.scrollLeft = cleft;
38935             }else if(cright > sright){
38936                 c.scrollLeft = cright-c.clientWidth;
38937             }
38938         }
38939          
38940         return el;
38941     },
38942
38943     updateColumns : function(){
38944         this.grid.stopEditing();
38945         var cm = this.grid.colModel, colIds = this.getColumnIds();
38946         //var totalWidth = cm.getTotalWidth();
38947         var pos = 0;
38948         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38949             //if(cm.isHidden(i)) continue;
38950             var w = cm.getColumnWidth(i);
38951             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38952             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38953         }
38954         this.updateSplitters();
38955     },
38956
38957     generateRules : function(cm){
38958         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38959         Roo.util.CSS.removeStyleSheet(rulesId);
38960         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38961             var cid = cm.getColumnId(i);
38962             var align = '';
38963             if(cm.config[i].align){
38964                 align = 'text-align:'+cm.config[i].align+';';
38965             }
38966             var hidden = '';
38967             if(cm.isHidden(i)){
38968                 hidden = 'display:none;';
38969             }
38970             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38971             ruleBuf.push(
38972                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38973                     this.hdSelector, cid, " {\n", align, width, "}\n",
38974                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
38975                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
38976         }
38977         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38978     },
38979
38980     updateSplitters : function(){
38981         var cm = this.cm, s = this.getSplitters();
38982         if(s){ // splitters not created yet
38983             var pos = 0, locked = true;
38984             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38985                 if(cm.isHidden(i)) {
38986                     continue;
38987                 }
38988                 var w = cm.getColumnWidth(i); // make sure it's a number
38989                 if(!cm.isLocked(i) && locked){
38990                     pos = 0;
38991                     locked = false;
38992                 }
38993                 pos += w;
38994                 s[i].style.left = (pos-this.splitOffset) + "px";
38995             }
38996         }
38997     },
38998
38999     handleHiddenChange : function(colModel, colIndex, hidden){
39000         if(hidden){
39001             this.hideColumn(colIndex);
39002         }else{
39003             this.unhideColumn(colIndex);
39004         }
39005     },
39006
39007     hideColumn : function(colIndex){
39008         var cid = this.getColumnId(colIndex);
39009         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
39010         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
39011         if(Roo.isSafari){
39012             this.updateHeaders();
39013         }
39014         this.updateSplitters();
39015         this.layout();
39016     },
39017
39018     unhideColumn : function(colIndex){
39019         var cid = this.getColumnId(colIndex);
39020         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
39021         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
39022
39023         if(Roo.isSafari){
39024             this.updateHeaders();
39025         }
39026         this.updateSplitters();
39027         this.layout();
39028     },
39029
39030     insertRows : function(dm, firstRow, lastRow, isUpdate){
39031         if(firstRow == 0 && lastRow == dm.getCount()-1){
39032             this.refresh();
39033         }else{
39034             if(!isUpdate){
39035                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
39036             }
39037             var s = this.getScrollState();
39038             var markup = this.renderRows(firstRow, lastRow);
39039             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
39040             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
39041             this.restoreScroll(s);
39042             if(!isUpdate){
39043                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
39044                 this.syncRowHeights(firstRow, lastRow);
39045                 this.stripeRows(firstRow);
39046                 this.layout();
39047             }
39048         }
39049     },
39050
39051     bufferRows : function(markup, target, index){
39052         var before = null, trows = target.rows, tbody = target.tBodies[0];
39053         if(index < trows.length){
39054             before = trows[index];
39055         }
39056         var b = document.createElement("div");
39057         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
39058         var rows = b.firstChild.rows;
39059         for(var i = 0, len = rows.length; i < len; i++){
39060             if(before){
39061                 tbody.insertBefore(rows[0], before);
39062             }else{
39063                 tbody.appendChild(rows[0]);
39064             }
39065         }
39066         b.innerHTML = "";
39067         b = null;
39068     },
39069
39070     deleteRows : function(dm, firstRow, lastRow){
39071         if(dm.getRowCount()<1){
39072             this.fireEvent("beforerefresh", this);
39073             this.mainBody.update("");
39074             this.lockedBody.update("");
39075             this.fireEvent("refresh", this);
39076         }else{
39077             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
39078             var bt = this.getBodyTable();
39079             var tbody = bt.firstChild;
39080             var rows = bt.rows;
39081             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
39082                 tbody.removeChild(rows[firstRow]);
39083             }
39084             this.stripeRows(firstRow);
39085             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
39086         }
39087     },
39088
39089     updateRows : function(dataSource, firstRow, lastRow){
39090         var s = this.getScrollState();
39091         this.refresh();
39092         this.restoreScroll(s);
39093     },
39094
39095     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
39096         if(!noRefresh){
39097            this.refresh();
39098         }
39099         this.updateHeaderSortState();
39100     },
39101
39102     getScrollState : function(){
39103         
39104         var sb = this.scroller.dom;
39105         return {left: sb.scrollLeft, top: sb.scrollTop};
39106     },
39107
39108     stripeRows : function(startRow){
39109         if(!this.grid.stripeRows || this.ds.getCount() < 1){
39110             return;
39111         }
39112         startRow = startRow || 0;
39113         var rows = this.getBodyTable().rows;
39114         var lrows = this.getLockedTable().rows;
39115         var cls = ' x-grid-row-alt ';
39116         for(var i = startRow, len = rows.length; i < len; i++){
39117             var row = rows[i], lrow = lrows[i];
39118             var isAlt = ((i+1) % 2 == 0);
39119             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
39120             if(isAlt == hasAlt){
39121                 continue;
39122             }
39123             if(isAlt){
39124                 row.className += " x-grid-row-alt";
39125             }else{
39126                 row.className = row.className.replace("x-grid-row-alt", "");
39127             }
39128             if(lrow){
39129                 lrow.className = row.className;
39130             }
39131         }
39132     },
39133
39134     restoreScroll : function(state){
39135         //Roo.log('GridView.restoreScroll');
39136         var sb = this.scroller.dom;
39137         sb.scrollLeft = state.left;
39138         sb.scrollTop = state.top;
39139         this.syncScroll();
39140     },
39141
39142     syncScroll : function(){
39143         //Roo.log('GridView.syncScroll');
39144         var sb = this.scroller.dom;
39145         var sh = this.mainHd.dom;
39146         var bs = this.mainBody.dom;
39147         var lv = this.lockedBody.dom;
39148         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
39149         lv.scrollTop = bs.scrollTop = sb.scrollTop;
39150     },
39151
39152     handleScroll : function(e){
39153         this.syncScroll();
39154         var sb = this.scroller.dom;
39155         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
39156         e.stopEvent();
39157     },
39158
39159     handleWheel : function(e){
39160         var d = e.getWheelDelta();
39161         this.scroller.dom.scrollTop -= d*22;
39162         // set this here to prevent jumpy scrolling on large tables
39163         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
39164         e.stopEvent();
39165     },
39166
39167     renderRows : function(startRow, endRow){
39168         // pull in all the crap needed to render rows
39169         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
39170         var colCount = cm.getColumnCount();
39171
39172         if(ds.getCount() < 1){
39173             return ["", ""];
39174         }
39175
39176         // build a map for all the columns
39177         var cs = [];
39178         for(var i = 0; i < colCount; i++){
39179             var name = cm.getDataIndex(i);
39180             cs[i] = {
39181                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
39182                 renderer : cm.getRenderer(i),
39183                 id : cm.getColumnId(i),
39184                 locked : cm.isLocked(i),
39185                 has_editor : cm.isCellEditable(i)
39186             };
39187         }
39188
39189         startRow = startRow || 0;
39190         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
39191
39192         // records to render
39193         var rs = ds.getRange(startRow, endRow);
39194
39195         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
39196     },
39197
39198     // As much as I hate to duplicate code, this was branched because FireFox really hates
39199     // [].join("") on strings. The performance difference was substantial enough to
39200     // branch this function
39201     doRender : Roo.isGecko ?
39202             function(cs, rs, ds, startRow, colCount, stripe){
39203                 var ts = this.templates, ct = ts.cell, rt = ts.row;
39204                 // buffers
39205                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
39206                 
39207                 var hasListener = this.grid.hasListener('rowclass');
39208                 var rowcfg = {};
39209                 for(var j = 0, len = rs.length; j < len; j++){
39210                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
39211                     for(var i = 0; i < colCount; i++){
39212                         c = cs[i];
39213                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
39214                         p.id = c.id;
39215                         p.css = p.attr = "";
39216                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
39217                         if(p.value == undefined || p.value === "") {
39218                             p.value = "&#160;";
39219                         }
39220                         if(c.has_editor){
39221                             p.css += ' x-grid-editable-cell';
39222                         }
39223                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
39224                             p.css +=  ' x-grid-dirty-cell';
39225                         }
39226                         var markup = ct.apply(p);
39227                         if(!c.locked){
39228                             cb+= markup;
39229                         }else{
39230                             lcb+= markup;
39231                         }
39232                     }
39233                     var alt = [];
39234                     if(stripe && ((rowIndex+1) % 2 == 0)){
39235                         alt.push("x-grid-row-alt")
39236                     }
39237                     if(r.dirty){
39238                         alt.push(  " x-grid-dirty-row");
39239                     }
39240                     rp.cells = lcb;
39241                     if(this.getRowClass){
39242                         alt.push(this.getRowClass(r, rowIndex));
39243                     }
39244                     if (hasListener) {
39245                         rowcfg = {
39246                              
39247                             record: r,
39248                             rowIndex : rowIndex,
39249                             rowClass : ''
39250                         };
39251                         this.grid.fireEvent('rowclass', this, rowcfg);
39252                         alt.push(rowcfg.rowClass);
39253                     }
39254                     rp.alt = alt.join(" ");
39255                     lbuf+= rt.apply(rp);
39256                     rp.cells = cb;
39257                     buf+=  rt.apply(rp);
39258                 }
39259                 return [lbuf, buf];
39260             } :
39261             function(cs, rs, ds, startRow, colCount, stripe){
39262                 var ts = this.templates, ct = ts.cell, rt = ts.row;
39263                 // buffers
39264                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
39265                 var hasListener = this.grid.hasListener('rowclass');
39266  
39267                 var rowcfg = {};
39268                 for(var j = 0, len = rs.length; j < len; j++){
39269                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
39270                     for(var i = 0; i < colCount; i++){
39271                         c = cs[i];
39272                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
39273                         p.id = c.id;
39274                         p.css = p.attr = "";
39275                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
39276                         if(p.value == undefined || p.value === "") {
39277                             p.value = "&#160;";
39278                         }
39279                         //Roo.log(c);
39280                          if(c.has_editor){
39281                             p.css += ' x-grid-editable-cell';
39282                         }
39283                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
39284                             p.css += ' x-grid-dirty-cell' 
39285                         }
39286                         
39287                         var markup = ct.apply(p);
39288                         if(!c.locked){
39289                             cb[cb.length] = markup;
39290                         }else{
39291                             lcb[lcb.length] = markup;
39292                         }
39293                     }
39294                     var alt = [];
39295                     if(stripe && ((rowIndex+1) % 2 == 0)){
39296                         alt.push( "x-grid-row-alt");
39297                     }
39298                     if(r.dirty){
39299                         alt.push(" x-grid-dirty-row");
39300                     }
39301                     rp.cells = lcb;
39302                     if(this.getRowClass){
39303                         alt.push( this.getRowClass(r, rowIndex));
39304                     }
39305                     if (hasListener) {
39306                         rowcfg = {
39307                              
39308                             record: r,
39309                             rowIndex : rowIndex,
39310                             rowClass : ''
39311                         };
39312                         this.grid.fireEvent('rowclass', this, rowcfg);
39313                         alt.push(rowcfg.rowClass);
39314                     }
39315                     
39316                     rp.alt = alt.join(" ");
39317                     rp.cells = lcb.join("");
39318                     lbuf[lbuf.length] = rt.apply(rp);
39319                     rp.cells = cb.join("");
39320                     buf[buf.length] =  rt.apply(rp);
39321                 }
39322                 return [lbuf.join(""), buf.join("")];
39323             },
39324
39325     renderBody : function(){
39326         var markup = this.renderRows();
39327         var bt = this.templates.body;
39328         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
39329     },
39330
39331     /**
39332      * Refreshes the grid
39333      * @param {Boolean} headersToo
39334      */
39335     refresh : function(headersToo){
39336         this.fireEvent("beforerefresh", this);
39337         this.grid.stopEditing();
39338         var result = this.renderBody();
39339         this.lockedBody.update(result[0]);
39340         this.mainBody.update(result[1]);
39341         if(headersToo === true){
39342             this.updateHeaders();
39343             this.updateColumns();
39344             this.updateSplitters();
39345             this.updateHeaderSortState();
39346         }
39347         this.syncRowHeights();
39348         this.layout();
39349         this.fireEvent("refresh", this);
39350     },
39351
39352     handleColumnMove : function(cm, oldIndex, newIndex){
39353         this.indexMap = null;
39354         var s = this.getScrollState();
39355         this.refresh(true);
39356         this.restoreScroll(s);
39357         this.afterMove(newIndex);
39358     },
39359
39360     afterMove : function(colIndex){
39361         if(this.enableMoveAnim && Roo.enableFx){
39362             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
39363         }
39364         // if multisort - fix sortOrder, and reload..
39365         if (this.grid.dataSource.multiSort) {
39366             // the we can call sort again..
39367             var dm = this.grid.dataSource;
39368             var cm = this.grid.colModel;
39369             var so = [];
39370             for(var i = 0; i < cm.config.length; i++ ) {
39371                 
39372                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
39373                     continue; // dont' bother, it's not in sort list or being set.
39374                 }
39375                 
39376                 so.push(cm.config[i].dataIndex);
39377             };
39378             dm.sortOrder = so;
39379             dm.load(dm.lastOptions);
39380             
39381             
39382         }
39383         
39384     },
39385
39386     updateCell : function(dm, rowIndex, dataIndex){
39387         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
39388         if(typeof colIndex == "undefined"){ // not present in grid
39389             return;
39390         }
39391         var cm = this.grid.colModel;
39392         var cell = this.getCell(rowIndex, colIndex);
39393         var cellText = this.getCellText(rowIndex, colIndex);
39394
39395         var p = {
39396             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
39397             id : cm.getColumnId(colIndex),
39398             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
39399         };
39400         var renderer = cm.getRenderer(colIndex);
39401         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
39402         if(typeof val == "undefined" || val === "") {
39403             val = "&#160;";
39404         }
39405         cellText.innerHTML = val;
39406         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
39407         this.syncRowHeights(rowIndex, rowIndex);
39408     },
39409
39410     calcColumnWidth : function(colIndex, maxRowsToMeasure){
39411         var maxWidth = 0;
39412         if(this.grid.autoSizeHeaders){
39413             var h = this.getHeaderCellMeasure(colIndex);
39414             maxWidth = Math.max(maxWidth, h.scrollWidth);
39415         }
39416         var tb, index;
39417         if(this.cm.isLocked(colIndex)){
39418             tb = this.getLockedTable();
39419             index = colIndex;
39420         }else{
39421             tb = this.getBodyTable();
39422             index = colIndex - this.cm.getLockedCount();
39423         }
39424         if(tb && tb.rows){
39425             var rows = tb.rows;
39426             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
39427             for(var i = 0; i < stopIndex; i++){
39428                 var cell = rows[i].childNodes[index].firstChild;
39429                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
39430             }
39431         }
39432         return maxWidth + /*margin for error in IE*/ 5;
39433     },
39434     /**
39435      * Autofit a column to its content.
39436      * @param {Number} colIndex
39437      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
39438      */
39439      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
39440          if(this.cm.isHidden(colIndex)){
39441              return; // can't calc a hidden column
39442          }
39443         if(forceMinSize){
39444             var cid = this.cm.getColumnId(colIndex);
39445             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
39446            if(this.grid.autoSizeHeaders){
39447                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
39448            }
39449         }
39450         var newWidth = this.calcColumnWidth(colIndex);
39451         this.cm.setColumnWidth(colIndex,
39452             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
39453         if(!suppressEvent){
39454             this.grid.fireEvent("columnresize", colIndex, newWidth);
39455         }
39456     },
39457
39458     /**
39459      * Autofits all columns to their content and then expands to fit any extra space in the grid
39460      */
39461      autoSizeColumns : function(){
39462         var cm = this.grid.colModel;
39463         var colCount = cm.getColumnCount();
39464         for(var i = 0; i < colCount; i++){
39465             this.autoSizeColumn(i, true, true);
39466         }
39467         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
39468             this.fitColumns();
39469         }else{
39470             this.updateColumns();
39471             this.layout();
39472         }
39473     },
39474
39475     /**
39476      * Autofits all columns to the grid's width proportionate with their current size
39477      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
39478      */
39479     fitColumns : function(reserveScrollSpace){
39480         var cm = this.grid.colModel;
39481         var colCount = cm.getColumnCount();
39482         var cols = [];
39483         var width = 0;
39484         var i, w;
39485         for (i = 0; i < colCount; i++){
39486             if(!cm.isHidden(i) && !cm.isFixed(i)){
39487                 w = cm.getColumnWidth(i);
39488                 cols.push(i);
39489                 cols.push(w);
39490                 width += w;
39491             }
39492         }
39493         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
39494         if(reserveScrollSpace){
39495             avail -= 17;
39496         }
39497         var frac = (avail - cm.getTotalWidth())/width;
39498         while (cols.length){
39499             w = cols.pop();
39500             i = cols.pop();
39501             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
39502         }
39503         this.updateColumns();
39504         this.layout();
39505     },
39506
39507     onRowSelect : function(rowIndex){
39508         var row = this.getRowComposite(rowIndex);
39509         row.addClass("x-grid-row-selected");
39510     },
39511
39512     onRowDeselect : function(rowIndex){
39513         var row = this.getRowComposite(rowIndex);
39514         row.removeClass("x-grid-row-selected");
39515     },
39516
39517     onCellSelect : function(row, col){
39518         var cell = this.getCell(row, col);
39519         if(cell){
39520             Roo.fly(cell).addClass("x-grid-cell-selected");
39521         }
39522     },
39523
39524     onCellDeselect : function(row, col){
39525         var cell = this.getCell(row, col);
39526         if(cell){
39527             Roo.fly(cell).removeClass("x-grid-cell-selected");
39528         }
39529     },
39530
39531     updateHeaderSortState : function(){
39532         
39533         // sort state can be single { field: xxx, direction : yyy}
39534         // or   { xxx=>ASC , yyy : DESC ..... }
39535         
39536         var mstate = {};
39537         if (!this.ds.multiSort) { 
39538             var state = this.ds.getSortState();
39539             if(!state){
39540                 return;
39541             }
39542             mstate[state.field] = state.direction;
39543             // FIXME... - this is not used here.. but might be elsewhere..
39544             this.sortState = state;
39545             
39546         } else {
39547             mstate = this.ds.sortToggle;
39548         }
39549         //remove existing sort classes..
39550         
39551         var sc = this.sortClasses;
39552         var hds = this.el.select(this.headerSelector).removeClass(sc);
39553         
39554         for(var f in mstate) {
39555         
39556             var sortColumn = this.cm.findColumnIndex(f);
39557             
39558             if(sortColumn != -1){
39559                 var sortDir = mstate[f];        
39560                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
39561             }
39562         }
39563         
39564          
39565         
39566     },
39567
39568
39569     handleHeaderClick : function(g, index,e){
39570         
39571         Roo.log("header click");
39572         
39573         if (Roo.isTouch) {
39574             // touch events on header are handled by context
39575             this.handleHdCtx(g,index,e);
39576             return;
39577         }
39578         
39579         
39580         if(this.headersDisabled){
39581             return;
39582         }
39583         var dm = g.dataSource, cm = g.colModel;
39584         if(!cm.isSortable(index)){
39585             return;
39586         }
39587         g.stopEditing();
39588         
39589         if (dm.multiSort) {
39590             // update the sortOrder
39591             var so = [];
39592             for(var i = 0; i < cm.config.length; i++ ) {
39593                 
39594                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
39595                     continue; // dont' bother, it's not in sort list or being set.
39596                 }
39597                 
39598                 so.push(cm.config[i].dataIndex);
39599             };
39600             dm.sortOrder = so;
39601         }
39602         
39603         
39604         dm.sort(cm.getDataIndex(index));
39605     },
39606
39607
39608     destroy : function(){
39609         if(this.colMenu){
39610             this.colMenu.removeAll();
39611             Roo.menu.MenuMgr.unregister(this.colMenu);
39612             this.colMenu.getEl().remove();
39613             delete this.colMenu;
39614         }
39615         if(this.hmenu){
39616             this.hmenu.removeAll();
39617             Roo.menu.MenuMgr.unregister(this.hmenu);
39618             this.hmenu.getEl().remove();
39619             delete this.hmenu;
39620         }
39621         if(this.grid.enableColumnMove){
39622             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39623             if(dds){
39624                 for(var dd in dds){
39625                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
39626                         var elid = dds[dd].dragElId;
39627                         dds[dd].unreg();
39628                         Roo.get(elid).remove();
39629                     } else if(dds[dd].config.isTarget){
39630                         dds[dd].proxyTop.remove();
39631                         dds[dd].proxyBottom.remove();
39632                         dds[dd].unreg();
39633                     }
39634                     if(Roo.dd.DDM.locationCache[dd]){
39635                         delete Roo.dd.DDM.locationCache[dd];
39636                     }
39637                 }
39638                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39639             }
39640         }
39641         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
39642         this.bind(null, null);
39643         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
39644     },
39645
39646     handleLockChange : function(){
39647         this.refresh(true);
39648     },
39649
39650     onDenyColumnLock : function(){
39651
39652     },
39653
39654     onDenyColumnHide : function(){
39655
39656     },
39657
39658     handleHdMenuClick : function(item){
39659         var index = this.hdCtxIndex;
39660         var cm = this.cm, ds = this.ds;
39661         switch(item.id){
39662             case "asc":
39663                 ds.sort(cm.getDataIndex(index), "ASC");
39664                 break;
39665             case "desc":
39666                 ds.sort(cm.getDataIndex(index), "DESC");
39667                 break;
39668             case "lock":
39669                 var lc = cm.getLockedCount();
39670                 if(cm.getColumnCount(true) <= lc+1){
39671                     this.onDenyColumnLock();
39672                     return;
39673                 }
39674                 if(lc != index){
39675                     cm.setLocked(index, true, true);
39676                     cm.moveColumn(index, lc);
39677                     this.grid.fireEvent("columnmove", index, lc);
39678                 }else{
39679                     cm.setLocked(index, true);
39680                 }
39681             break;
39682             case "unlock":
39683                 var lc = cm.getLockedCount();
39684                 if((lc-1) != index){
39685                     cm.setLocked(index, false, true);
39686                     cm.moveColumn(index, lc-1);
39687                     this.grid.fireEvent("columnmove", index, lc-1);
39688                 }else{
39689                     cm.setLocked(index, false);
39690                 }
39691             break;
39692             case 'wider': // used to expand cols on touch..
39693             case 'narrow':
39694                 var cw = cm.getColumnWidth(index);
39695                 cw += (item.id == 'wider' ? 1 : -1) * 50;
39696                 cw = Math.max(0, cw);
39697                 cw = Math.min(cw,4000);
39698                 cm.setColumnWidth(index, cw);
39699                 break;
39700                 
39701             default:
39702                 index = cm.getIndexById(item.id.substr(4));
39703                 if(index != -1){
39704                     if(item.checked && cm.getColumnCount(true) <= 1){
39705                         this.onDenyColumnHide();
39706                         return false;
39707                     }
39708                     cm.setHidden(index, item.checked);
39709                 }
39710         }
39711         return true;
39712     },
39713
39714     beforeColMenuShow : function(){
39715         var cm = this.cm,  colCount = cm.getColumnCount();
39716         this.colMenu.removeAll();
39717         
39718         var items = [];
39719         for(var i = 0; i < colCount; i++){
39720             items.push({
39721                 id: "col-"+cm.getColumnId(i),
39722                 text: cm.getColumnHeader(i),
39723                 checked: !cm.isHidden(i),
39724                 hideOnClick:false
39725             });
39726         }
39727         
39728         if (this.grid.sortColMenu) {
39729             items.sort(function(a,b) {
39730                 if (a.text == b.text) {
39731                     return 0;
39732                 }
39733                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39734             });
39735         }
39736         
39737         for(var i = 0; i < colCount; i++){
39738             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39739         }
39740     },
39741
39742     handleHdCtx : function(g, index, e){
39743         e.stopEvent();
39744         var hd = this.getHeaderCell(index);
39745         this.hdCtxIndex = index;
39746         var ms = this.hmenu.items, cm = this.cm;
39747         ms.get("asc").setDisabled(!cm.isSortable(index));
39748         ms.get("desc").setDisabled(!cm.isSortable(index));
39749         if(this.grid.enableColLock !== false){
39750             ms.get("lock").setDisabled(cm.isLocked(index));
39751             ms.get("unlock").setDisabled(!cm.isLocked(index));
39752         }
39753         this.hmenu.show(hd, "tl-bl");
39754     },
39755
39756     handleHdOver : function(e){
39757         var hd = this.findHeaderCell(e.getTarget());
39758         if(hd && !this.headersDisabled){
39759             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39760                this.fly(hd).addClass("x-grid-hd-over");
39761             }
39762         }
39763     },
39764
39765     handleHdOut : function(e){
39766         var hd = this.findHeaderCell(e.getTarget());
39767         if(hd){
39768             this.fly(hd).removeClass("x-grid-hd-over");
39769         }
39770     },
39771
39772     handleSplitDblClick : function(e, t){
39773         var i = this.getCellIndex(t);
39774         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39775             this.autoSizeColumn(i, true);
39776             this.layout();
39777         }
39778     },
39779
39780     render : function(){
39781
39782         var cm = this.cm;
39783         var colCount = cm.getColumnCount();
39784
39785         if(this.grid.monitorWindowResize === true){
39786             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39787         }
39788         var header = this.renderHeaders();
39789         var body = this.templates.body.apply({rows:""});
39790         var html = this.templates.master.apply({
39791             lockedBody: body,
39792             body: body,
39793             lockedHeader: header[0],
39794             header: header[1]
39795         });
39796
39797         //this.updateColumns();
39798
39799         this.grid.getGridEl().dom.innerHTML = html;
39800
39801         this.initElements();
39802         
39803         // a kludge to fix the random scolling effect in webkit
39804         this.el.on("scroll", function() {
39805             this.el.dom.scrollTop=0; // hopefully not recursive..
39806         },this);
39807
39808         this.scroller.on("scroll", this.handleScroll, this);
39809         this.lockedBody.on("mousewheel", this.handleWheel, this);
39810         this.mainBody.on("mousewheel", this.handleWheel, this);
39811
39812         this.mainHd.on("mouseover", this.handleHdOver, this);
39813         this.mainHd.on("mouseout", this.handleHdOut, this);
39814         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39815                 {delegate: "."+this.splitClass});
39816
39817         this.lockedHd.on("mouseover", this.handleHdOver, this);
39818         this.lockedHd.on("mouseout", this.handleHdOut, this);
39819         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39820                 {delegate: "."+this.splitClass});
39821
39822         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39823             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39824         }
39825
39826         this.updateSplitters();
39827
39828         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39829             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39830             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39831         }
39832
39833         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39834             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39835             this.hmenu.add(
39836                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39837                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39838             );
39839             if(this.grid.enableColLock !== false){
39840                 this.hmenu.add('-',
39841                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39842                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39843                 );
39844             }
39845             if (Roo.isTouch) {
39846                  this.hmenu.add('-',
39847                     {id:"wider", text: this.columnsWiderText},
39848                     {id:"narrow", text: this.columnsNarrowText }
39849                 );
39850                 
39851                  
39852             }
39853             
39854             if(this.grid.enableColumnHide !== false){
39855
39856                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39857                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39858                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39859
39860                 this.hmenu.add('-',
39861                     {id:"columns", text: this.columnsText, menu: this.colMenu}
39862                 );
39863             }
39864             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39865
39866             this.grid.on("headercontextmenu", this.handleHdCtx, this);
39867         }
39868
39869         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39870             this.dd = new Roo.grid.GridDragZone(this.grid, {
39871                 ddGroup : this.grid.ddGroup || 'GridDD'
39872             });
39873             
39874         }
39875
39876         /*
39877         for(var i = 0; i < colCount; i++){
39878             if(cm.isHidden(i)){
39879                 this.hideColumn(i);
39880             }
39881             if(cm.config[i].align){
39882                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39883                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39884             }
39885         }*/
39886         
39887         this.updateHeaderSortState();
39888
39889         this.beforeInitialResize();
39890         this.layout(true);
39891
39892         // two part rendering gives faster view to the user
39893         this.renderPhase2.defer(1, this);
39894     },
39895
39896     renderPhase2 : function(){
39897         // render the rows now
39898         this.refresh();
39899         if(this.grid.autoSizeColumns){
39900             this.autoSizeColumns();
39901         }
39902     },
39903
39904     beforeInitialResize : function(){
39905
39906     },
39907
39908     onColumnSplitterMoved : function(i, w){
39909         this.userResized = true;
39910         var cm = this.grid.colModel;
39911         cm.setColumnWidth(i, w, true);
39912         var cid = cm.getColumnId(i);
39913         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39914         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39915         this.updateSplitters();
39916         this.layout();
39917         this.grid.fireEvent("columnresize", i, w);
39918     },
39919
39920     syncRowHeights : function(startIndex, endIndex){
39921         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39922             startIndex = startIndex || 0;
39923             var mrows = this.getBodyTable().rows;
39924             var lrows = this.getLockedTable().rows;
39925             var len = mrows.length-1;
39926             endIndex = Math.min(endIndex || len, len);
39927             for(var i = startIndex; i <= endIndex; i++){
39928                 var m = mrows[i], l = lrows[i];
39929                 var h = Math.max(m.offsetHeight, l.offsetHeight);
39930                 m.style.height = l.style.height = h + "px";
39931             }
39932         }
39933     },
39934
39935     layout : function(initialRender, is2ndPass)
39936     {
39937         var g = this.grid;
39938         var auto = g.autoHeight;
39939         var scrollOffset = 16;
39940         var c = g.getGridEl(), cm = this.cm,
39941                 expandCol = g.autoExpandColumn,
39942                 gv = this;
39943         //c.beginMeasure();
39944
39945         if(!c.dom.offsetWidth){ // display:none?
39946             if(initialRender){
39947                 this.lockedWrap.show();
39948                 this.mainWrap.show();
39949             }
39950             return;
39951         }
39952
39953         var hasLock = this.cm.isLocked(0);
39954
39955         var tbh = this.headerPanel.getHeight();
39956         var bbh = this.footerPanel.getHeight();
39957
39958         if(auto){
39959             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39960             var newHeight = ch + c.getBorderWidth("tb");
39961             if(g.maxHeight){
39962                 newHeight = Math.min(g.maxHeight, newHeight);
39963             }
39964             c.setHeight(newHeight);
39965         }
39966
39967         if(g.autoWidth){
39968             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39969         }
39970
39971         var s = this.scroller;
39972
39973         var csize = c.getSize(true);
39974
39975         this.el.setSize(csize.width, csize.height);
39976
39977         this.headerPanel.setWidth(csize.width);
39978         this.footerPanel.setWidth(csize.width);
39979
39980         var hdHeight = this.mainHd.getHeight();
39981         var vw = csize.width;
39982         var vh = csize.height - (tbh + bbh);
39983
39984         s.setSize(vw, vh);
39985
39986         var bt = this.getBodyTable();
39987         
39988         if(cm.getLockedCount() == cm.config.length){
39989             bt = this.getLockedTable();
39990         }
39991         
39992         var ltWidth = hasLock ?
39993                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39994
39995         var scrollHeight = bt.offsetHeight;
39996         var scrollWidth = ltWidth + bt.offsetWidth;
39997         var vscroll = false, hscroll = false;
39998
39999         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
40000
40001         var lw = this.lockedWrap, mw = this.mainWrap;
40002         var lb = this.lockedBody, mb = this.mainBody;
40003
40004         setTimeout(function(){
40005             var t = s.dom.offsetTop;
40006             var w = s.dom.clientWidth,
40007                 h = s.dom.clientHeight;
40008
40009             lw.setTop(t);
40010             lw.setSize(ltWidth, h);
40011
40012             mw.setLeftTop(ltWidth, t);
40013             mw.setSize(w-ltWidth, h);
40014
40015             lb.setHeight(h-hdHeight);
40016             mb.setHeight(h-hdHeight);
40017
40018             if(is2ndPass !== true && !gv.userResized && expandCol){
40019                 // high speed resize without full column calculation
40020                 
40021                 var ci = cm.getIndexById(expandCol);
40022                 if (ci < 0) {
40023                     ci = cm.findColumnIndex(expandCol);
40024                 }
40025                 ci = Math.max(0, ci); // make sure it's got at least the first col.
40026                 var expandId = cm.getColumnId(ci);
40027                 var  tw = cm.getTotalWidth(false);
40028                 var currentWidth = cm.getColumnWidth(ci);
40029                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
40030                 if(currentWidth != cw){
40031                     cm.setColumnWidth(ci, cw, true);
40032                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
40033                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
40034                     gv.updateSplitters();
40035                     gv.layout(false, true);
40036                 }
40037             }
40038
40039             if(initialRender){
40040                 lw.show();
40041                 mw.show();
40042             }
40043             //c.endMeasure();
40044         }, 10);
40045     },
40046
40047     onWindowResize : function(){
40048         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
40049             return;
40050         }
40051         this.layout();
40052     },
40053
40054     appendFooter : function(parentEl){
40055         return null;
40056     },
40057
40058     sortAscText : "Sort Ascending",
40059     sortDescText : "Sort Descending",
40060     lockText : "Lock Column",
40061     unlockText : "Unlock Column",
40062     columnsText : "Columns",
40063  
40064     columnsWiderText : "Wider",
40065     columnsNarrowText : "Thinner"
40066 });
40067
40068
40069 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
40070     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
40071     this.proxy.el.addClass('x-grid3-col-dd');
40072 };
40073
40074 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
40075     handleMouseDown : function(e){
40076
40077     },
40078
40079     callHandleMouseDown : function(e){
40080         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
40081     }
40082 });
40083 /*
40084  * Based on:
40085  * Ext JS Library 1.1.1
40086  * Copyright(c) 2006-2007, Ext JS, LLC.
40087  *
40088  * Originally Released Under LGPL - original licence link has changed is not relivant.
40089  *
40090  * Fork - LGPL
40091  * <script type="text/javascript">
40092  */
40093  /**
40094  * @extends Roo.dd.DDProxy
40095  * @class Roo.grid.SplitDragZone
40096  * Support for Column Header resizing
40097  * @constructor
40098  * @param {Object} config
40099  */
40100 // private
40101 // This is a support class used internally by the Grid components
40102 Roo.grid.SplitDragZone = function(grid, hd, hd2){
40103     this.grid = grid;
40104     this.view = grid.getView();
40105     this.proxy = this.view.resizeProxy;
40106     Roo.grid.SplitDragZone.superclass.constructor.call(
40107         this,
40108         hd, // ID
40109         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
40110         {  // CONFIG
40111             dragElId : Roo.id(this.proxy.dom),
40112             resizeFrame:false
40113         }
40114     );
40115     
40116     this.setHandleElId(Roo.id(hd));
40117     if (hd2 !== false) {
40118         this.setOuterHandleElId(Roo.id(hd2));
40119     }
40120     
40121     this.scroll = false;
40122 };
40123 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
40124     fly: Roo.Element.fly,
40125
40126     b4StartDrag : function(x, y){
40127         this.view.headersDisabled = true;
40128         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
40129                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
40130         );
40131         this.proxy.setHeight(h);
40132         
40133         // for old system colWidth really stored the actual width?
40134         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
40135         // which in reality did not work.. - it worked only for fixed sizes
40136         // for resizable we need to use actual sizes.
40137         var w = this.cm.getColumnWidth(this.cellIndex);
40138         if (!this.view.mainWrap) {
40139             // bootstrap.
40140             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
40141         }
40142         
40143         
40144         
40145         // this was w-this.grid.minColumnWidth;
40146         // doesnt really make sense? - w = thie curren width or the rendered one?
40147         var minw = Math.max(w-this.grid.minColumnWidth, 0);
40148         this.resetConstraints();
40149         this.setXConstraint(minw, 1000);
40150         this.setYConstraint(0, 0);
40151         this.minX = x - minw;
40152         this.maxX = x + 1000;
40153         this.startPos = x;
40154         if (!this.view.mainWrap) { // this is Bootstrap code..
40155             this.getDragEl().style.display='block';
40156         }
40157         
40158         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
40159     },
40160
40161
40162     handleMouseDown : function(e){
40163         ev = Roo.EventObject.setEvent(e);
40164         var t = this.fly(ev.getTarget());
40165         if(t.hasClass("x-grid-split")){
40166             this.cellIndex = this.view.getCellIndex(t.dom);
40167             this.split = t.dom;
40168             this.cm = this.grid.colModel;
40169             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
40170                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
40171             }
40172         }
40173     },
40174
40175     endDrag : function(e){
40176         this.view.headersDisabled = false;
40177         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
40178         var diff = endX - this.startPos;
40179         // 
40180         var w = this.cm.getColumnWidth(this.cellIndex);
40181         if (!this.view.mainWrap) {
40182             w = 0;
40183         }
40184         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
40185     },
40186
40187     autoOffset : function(){
40188         this.setDelta(0,0);
40189     }
40190 });/*
40191  * Based on:
40192  * Ext JS Library 1.1.1
40193  * Copyright(c) 2006-2007, Ext JS, LLC.
40194  *
40195  * Originally Released Under LGPL - original licence link has changed is not relivant.
40196  *
40197  * Fork - LGPL
40198  * <script type="text/javascript">
40199  */
40200  
40201 // private
40202 // This is a support class used internally by the Grid components
40203 Roo.grid.GridDragZone = function(grid, config){
40204     this.view = grid.getView();
40205     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
40206     if(this.view.lockedBody){
40207         this.setHandleElId(Roo.id(this.view.mainBody.dom));
40208         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
40209     }
40210     this.scroll = false;
40211     this.grid = grid;
40212     this.ddel = document.createElement('div');
40213     this.ddel.className = 'x-grid-dd-wrap';
40214 };
40215
40216 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
40217     ddGroup : "GridDD",
40218
40219     getDragData : function(e){
40220         var t = Roo.lib.Event.getTarget(e);
40221         var rowIndex = this.view.findRowIndex(t);
40222         var sm = this.grid.selModel;
40223             
40224         //Roo.log(rowIndex);
40225         
40226         if (sm.getSelectedCell) {
40227             // cell selection..
40228             if (!sm.getSelectedCell()) {
40229                 return false;
40230             }
40231             if (rowIndex != sm.getSelectedCell()[0]) {
40232                 return false;
40233             }
40234         
40235         }
40236         if (sm.getSelections && sm.getSelections().length < 1) {
40237             return false;
40238         }
40239         
40240         
40241         // before it used to all dragging of unseleted... - now we dont do that.
40242         if(rowIndex !== false){
40243             
40244             // if editorgrid.. 
40245             
40246             
40247             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
40248                
40249             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
40250               //  
40251             //}
40252             if (e.hasModifier()){
40253                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
40254             }
40255             
40256             Roo.log("getDragData");
40257             
40258             return {
40259                 grid: this.grid,
40260                 ddel: this.ddel,
40261                 rowIndex: rowIndex,
40262                 selections: sm.getSelections ? sm.getSelections() : (
40263                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
40264             };
40265         }
40266         return false;
40267     },
40268     
40269     
40270     onInitDrag : function(e){
40271         var data = this.dragData;
40272         this.ddel.innerHTML = this.grid.getDragDropText();
40273         this.proxy.update(this.ddel);
40274         // fire start drag?
40275     },
40276
40277     afterRepair : function(){
40278         this.dragging = false;
40279     },
40280
40281     getRepairXY : function(e, data){
40282         return false;
40283     },
40284
40285     onEndDrag : function(data, e){
40286         // fire end drag?
40287     },
40288
40289     onValidDrop : function(dd, e, id){
40290         // fire drag drop?
40291         this.hideProxy();
40292     },
40293
40294     beforeInvalidDrop : function(e, id){
40295
40296     }
40297 });/*
40298  * Based on:
40299  * Ext JS Library 1.1.1
40300  * Copyright(c) 2006-2007, Ext JS, LLC.
40301  *
40302  * Originally Released Under LGPL - original licence link has changed is not relivant.
40303  *
40304  * Fork - LGPL
40305  * <script type="text/javascript">
40306  */
40307  
40308
40309 /**
40310  * @class Roo.grid.ColumnModel
40311  * @extends Roo.util.Observable
40312  * This is the default implementation of a ColumnModel used by the Grid. It defines
40313  * the columns in the grid.
40314  * <br>Usage:<br>
40315  <pre><code>
40316  var colModel = new Roo.grid.ColumnModel([
40317         {header: "Ticker", width: 60, sortable: true, locked: true},
40318         {header: "Company Name", width: 150, sortable: true},
40319         {header: "Market Cap.", width: 100, sortable: true},
40320         {header: "$ Sales", width: 100, sortable: true, renderer: money},
40321         {header: "Employees", width: 100, sortable: true, resizable: false}
40322  ]);
40323  </code></pre>
40324  * <p>
40325  
40326  * The config options listed for this class are options which may appear in each
40327  * individual column definition.
40328  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
40329  * @constructor
40330  * @param {Object} config An Array of column config objects. See this class's
40331  * config objects for details.
40332 */
40333 Roo.grid.ColumnModel = function(config){
40334         /**
40335      * The config passed into the constructor
40336      */
40337     this.config = []; //config;
40338     this.lookup = {};
40339
40340     // if no id, create one
40341     // if the column does not have a dataIndex mapping,
40342     // map it to the order it is in the config
40343     for(var i = 0, len = config.length; i < len; i++){
40344         this.addColumn(config[i]);
40345         
40346     }
40347
40348     /**
40349      * The width of columns which have no width specified (defaults to 100)
40350      * @type Number
40351      */
40352     this.defaultWidth = 100;
40353
40354     /**
40355      * Default sortable of columns which have no sortable specified (defaults to false)
40356      * @type Boolean
40357      */
40358     this.defaultSortable = false;
40359
40360     this.addEvents({
40361         /**
40362              * @event widthchange
40363              * Fires when the width of a column changes.
40364              * @param {ColumnModel} this
40365              * @param {Number} columnIndex The column index
40366              * @param {Number} newWidth The new width
40367              */
40368             "widthchange": true,
40369         /**
40370              * @event headerchange
40371              * Fires when the text of a header changes.
40372              * @param {ColumnModel} this
40373              * @param {Number} columnIndex The column index
40374              * @param {Number} newText The new header text
40375              */
40376             "headerchange": true,
40377         /**
40378              * @event hiddenchange
40379              * Fires when a column is hidden or "unhidden".
40380              * @param {ColumnModel} this
40381              * @param {Number} columnIndex The column index
40382              * @param {Boolean} hidden true if hidden, false otherwise
40383              */
40384             "hiddenchange": true,
40385             /**
40386          * @event columnmoved
40387          * Fires when a column is moved.
40388          * @param {ColumnModel} this
40389          * @param {Number} oldIndex
40390          * @param {Number} newIndex
40391          */
40392         "columnmoved" : true,
40393         /**
40394          * @event columlockchange
40395          * Fires when a column's locked state is changed
40396          * @param {ColumnModel} this
40397          * @param {Number} colIndex
40398          * @param {Boolean} locked true if locked
40399          */
40400         "columnlockchange" : true
40401     });
40402     Roo.grid.ColumnModel.superclass.constructor.call(this);
40403 };
40404 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
40405     /**
40406      * @cfg {String} header [required] The header text to display in the Grid view.
40407      */
40408         /**
40409      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
40410      */
40411         /**
40412      * @cfg {String} smHeader Header at Bootsrap Small width
40413      */
40414         /**
40415      * @cfg {String} mdHeader Header at Bootsrap Medium width
40416      */
40417         /**
40418      * @cfg {String} lgHeader Header at Bootsrap Large width
40419      */
40420         /**
40421      * @cfg {String} xlHeader Header at Bootsrap extra Large width
40422      */
40423     /**
40424      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
40425      * {@link Roo.data.Record} definition from which to draw the column's value. If not
40426      * specified, the column's index is used as an index into the Record's data Array.
40427      */
40428     /**
40429      * @cfg {Number} width  The initial width in pixels of the column. Using this
40430      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
40431      */
40432     /**
40433      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
40434      * Defaults to the value of the {@link #defaultSortable} property.
40435      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
40436      */
40437     /**
40438      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
40439      */
40440     /**
40441      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
40442      */
40443     /**
40444      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
40445      */
40446     /**
40447      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
40448      */
40449     /**
40450      * @cfg {Function} renderer A function used to generate HTML markup for a cell
40451      * given the cell's data value. See {@link #setRenderer}. If not specified, the
40452      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
40453      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
40454      */
40455        /**
40456      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
40457      */
40458     /**
40459      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
40460      */
40461     /**
40462      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
40463      */
40464     /**
40465      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
40466      */
40467     /**
40468      * @cfg {String} tooltip mouse over tooltip text
40469      */
40470     /**
40471      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
40472      */
40473     /**
40474      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
40475      */
40476     /**
40477      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
40478      */
40479     /**
40480      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
40481      */
40482         /**
40483      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
40484      */
40485     /**
40486      * Returns the id of the column at the specified index.
40487      * @param {Number} index The column index
40488      * @return {String} the id
40489      */
40490     getColumnId : function(index){
40491         return this.config[index].id;
40492     },
40493
40494     /**
40495      * Returns the column for a specified id.
40496      * @param {String} id The column id
40497      * @return {Object} the column
40498      */
40499     getColumnById : function(id){
40500         return this.lookup[id];
40501     },
40502
40503     
40504     /**
40505      * Returns the column Object for a specified dataIndex.
40506      * @param {String} dataIndex The column dataIndex
40507      * @return {Object|Boolean} the column or false if not found
40508      */
40509     getColumnByDataIndex: function(dataIndex){
40510         var index = this.findColumnIndex(dataIndex);
40511         return index > -1 ? this.config[index] : false;
40512     },
40513     
40514     /**
40515      * Returns the index for a specified column id.
40516      * @param {String} id The column id
40517      * @return {Number} the index, or -1 if not found
40518      */
40519     getIndexById : function(id){
40520         for(var i = 0, len = this.config.length; i < len; i++){
40521             if(this.config[i].id == id){
40522                 return i;
40523             }
40524         }
40525         return -1;
40526     },
40527     
40528     /**
40529      * Returns the index for a specified column dataIndex.
40530      * @param {String} dataIndex The column dataIndex
40531      * @return {Number} the index, or -1 if not found
40532      */
40533     
40534     findColumnIndex : function(dataIndex){
40535         for(var i = 0, len = this.config.length; i < len; i++){
40536             if(this.config[i].dataIndex == dataIndex){
40537                 return i;
40538             }
40539         }
40540         return -1;
40541     },
40542     
40543     
40544     moveColumn : function(oldIndex, newIndex){
40545         var c = this.config[oldIndex];
40546         this.config.splice(oldIndex, 1);
40547         this.config.splice(newIndex, 0, c);
40548         this.dataMap = null;
40549         this.fireEvent("columnmoved", this, oldIndex, newIndex);
40550     },
40551
40552     isLocked : function(colIndex){
40553         return this.config[colIndex].locked === true;
40554     },
40555
40556     setLocked : function(colIndex, value, suppressEvent){
40557         if(this.isLocked(colIndex) == value){
40558             return;
40559         }
40560         this.config[colIndex].locked = value;
40561         if(!suppressEvent){
40562             this.fireEvent("columnlockchange", this, colIndex, value);
40563         }
40564     },
40565
40566     getTotalLockedWidth : function(){
40567         var totalWidth = 0;
40568         for(var i = 0; i < this.config.length; i++){
40569             if(this.isLocked(i) && !this.isHidden(i)){
40570                 this.totalWidth += this.getColumnWidth(i);
40571             }
40572         }
40573         return totalWidth;
40574     },
40575
40576     getLockedCount : function(){
40577         for(var i = 0, len = this.config.length; i < len; i++){
40578             if(!this.isLocked(i)){
40579                 return i;
40580             }
40581         }
40582         
40583         return this.config.length;
40584     },
40585
40586     /**
40587      * Returns the number of columns.
40588      * @return {Number}
40589      */
40590     getColumnCount : function(visibleOnly){
40591         if(visibleOnly === true){
40592             var c = 0;
40593             for(var i = 0, len = this.config.length; i < len; i++){
40594                 if(!this.isHidden(i)){
40595                     c++;
40596                 }
40597             }
40598             return c;
40599         }
40600         return this.config.length;
40601     },
40602
40603     /**
40604      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
40605      * @param {Function} fn
40606      * @param {Object} scope (optional)
40607      * @return {Array} result
40608      */
40609     getColumnsBy : function(fn, scope){
40610         var r = [];
40611         for(var i = 0, len = this.config.length; i < len; i++){
40612             var c = this.config[i];
40613             if(fn.call(scope||this, c, i) === true){
40614                 r[r.length] = c;
40615             }
40616         }
40617         return r;
40618     },
40619
40620     /**
40621      * Returns true if the specified column is sortable.
40622      * @param {Number} col The column index
40623      * @return {Boolean}
40624      */
40625     isSortable : function(col){
40626         if(typeof this.config[col].sortable == "undefined"){
40627             return this.defaultSortable;
40628         }
40629         return this.config[col].sortable;
40630     },
40631
40632     /**
40633      * Returns the rendering (formatting) function defined for the column.
40634      * @param {Number} col The column index.
40635      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
40636      */
40637     getRenderer : function(col){
40638         if(!this.config[col].renderer){
40639             return Roo.grid.ColumnModel.defaultRenderer;
40640         }
40641         return this.config[col].renderer;
40642     },
40643
40644     /**
40645      * Sets the rendering (formatting) function for a column.
40646      * @param {Number} col The column index
40647      * @param {Function} fn The function to use to process the cell's raw data
40648      * to return HTML markup for the grid view. The render function is called with
40649      * the following parameters:<ul>
40650      * <li>Data value.</li>
40651      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
40652      * <li>css A CSS style string to apply to the table cell.</li>
40653      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
40654      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
40655      * <li>Row index</li>
40656      * <li>Column index</li>
40657      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
40658      */
40659     setRenderer : function(col, fn){
40660         this.config[col].renderer = fn;
40661     },
40662
40663     /**
40664      * Returns the width for the specified column.
40665      * @param {Number} col The column index
40666      * @param (optional) {String} gridSize bootstrap width size.
40667      * @return {Number}
40668      */
40669     getColumnWidth : function(col, gridSize)
40670         {
40671                 var cfg = this.config[col];
40672                 
40673                 if (typeof(gridSize) == 'undefined') {
40674                         return cfg.width * 1 || this.defaultWidth;
40675                 }
40676                 if (gridSize === false) { // if we set it..
40677                         return cfg.width || false;
40678                 }
40679                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40680                 
40681                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40682                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40683                                 continue;
40684                         }
40685                         return cfg[ sizes[i] ];
40686                 }
40687                 return 1;
40688                 
40689     },
40690
40691     /**
40692      * Sets the width for a column.
40693      * @param {Number} col The column index
40694      * @param {Number} width The new width
40695      */
40696     setColumnWidth : function(col, width, suppressEvent){
40697         this.config[col].width = width;
40698         this.totalWidth = null;
40699         if(!suppressEvent){
40700              this.fireEvent("widthchange", this, col, width);
40701         }
40702     },
40703
40704     /**
40705      * Returns the total width of all columns.
40706      * @param {Boolean} includeHidden True to include hidden column widths
40707      * @return {Number}
40708      */
40709     getTotalWidth : function(includeHidden){
40710         if(!this.totalWidth){
40711             this.totalWidth = 0;
40712             for(var i = 0, len = this.config.length; i < len; i++){
40713                 if(includeHidden || !this.isHidden(i)){
40714                     this.totalWidth += this.getColumnWidth(i);
40715                 }
40716             }
40717         }
40718         return this.totalWidth;
40719     },
40720
40721     /**
40722      * Returns the header for the specified column.
40723      * @param {Number} col The column index
40724      * @return {String}
40725      */
40726     getColumnHeader : function(col){
40727         return this.config[col].header;
40728     },
40729
40730     /**
40731      * Sets the header for a column.
40732      * @param {Number} col The column index
40733      * @param {String} header The new header
40734      */
40735     setColumnHeader : function(col, header){
40736         this.config[col].header = header;
40737         this.fireEvent("headerchange", this, col, header);
40738     },
40739
40740     /**
40741      * Returns the tooltip for the specified column.
40742      * @param {Number} col The column index
40743      * @return {String}
40744      */
40745     getColumnTooltip : function(col){
40746             return this.config[col].tooltip;
40747     },
40748     /**
40749      * Sets the tooltip for a column.
40750      * @param {Number} col The column index
40751      * @param {String} tooltip The new tooltip
40752      */
40753     setColumnTooltip : function(col, tooltip){
40754             this.config[col].tooltip = tooltip;
40755     },
40756
40757     /**
40758      * Returns the dataIndex for the specified column.
40759      * @param {Number} col The column index
40760      * @return {Number}
40761      */
40762     getDataIndex : function(col){
40763         return this.config[col].dataIndex;
40764     },
40765
40766     /**
40767      * Sets the dataIndex for a column.
40768      * @param {Number} col The column index
40769      * @param {Number} dataIndex The new dataIndex
40770      */
40771     setDataIndex : function(col, dataIndex){
40772         this.config[col].dataIndex = dataIndex;
40773     },
40774
40775     
40776     
40777     /**
40778      * Returns true if the cell is editable.
40779      * @param {Number} colIndex The column index
40780      * @param {Number} rowIndex The row index - this is nto actually used..?
40781      * @return {Boolean}
40782      */
40783     isCellEditable : function(colIndex, rowIndex){
40784         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40785     },
40786
40787     /**
40788      * Returns the editor defined for the cell/column.
40789      * return false or null to disable editing.
40790      * @param {Number} colIndex The column index
40791      * @param {Number} rowIndex The row index
40792      * @return {Object}
40793      */
40794     getCellEditor : function(colIndex, rowIndex){
40795         return this.config[colIndex].editor;
40796     },
40797
40798     /**
40799      * Sets if a column is editable.
40800      * @param {Number} col The column index
40801      * @param {Boolean} editable True if the column is editable
40802      */
40803     setEditable : function(col, editable){
40804         this.config[col].editable = editable;
40805     },
40806
40807
40808     /**
40809      * Returns true if the column is hidden.
40810      * @param {Number} colIndex The column index
40811      * @return {Boolean}
40812      */
40813     isHidden : function(colIndex){
40814         return this.config[colIndex].hidden;
40815     },
40816
40817
40818     /**
40819      * Returns true if the column width cannot be changed
40820      */
40821     isFixed : function(colIndex){
40822         return this.config[colIndex].fixed;
40823     },
40824
40825     /**
40826      * Returns true if the column can be resized
40827      * @return {Boolean}
40828      */
40829     isResizable : function(colIndex){
40830         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40831     },
40832     /**
40833      * Sets if a column is hidden.
40834      * @param {Number} colIndex The column index
40835      * @param {Boolean} hidden True if the column is hidden
40836      */
40837     setHidden : function(colIndex, hidden){
40838         this.config[colIndex].hidden = hidden;
40839         this.totalWidth = null;
40840         this.fireEvent("hiddenchange", this, colIndex, hidden);
40841     },
40842
40843     /**
40844      * Sets the editor for a column.
40845      * @param {Number} col The column index
40846      * @param {Object} editor The editor object
40847      */
40848     setEditor : function(col, editor){
40849         this.config[col].editor = editor;
40850     },
40851     /**
40852      * Add a column (experimental...) - defaults to adding to the end..
40853      * @param {Object} config 
40854     */
40855     addColumn : function(c)
40856     {
40857     
40858         var i = this.config.length;
40859         this.config[i] = c;
40860         
40861         if(typeof c.dataIndex == "undefined"){
40862             c.dataIndex = i;
40863         }
40864         if(typeof c.renderer == "string"){
40865             c.renderer = Roo.util.Format[c.renderer];
40866         }
40867         if(typeof c.id == "undefined"){
40868             c.id = Roo.id();
40869         }
40870         if(c.editor && c.editor.xtype){
40871             c.editor  = Roo.factory(c.editor, Roo.grid);
40872         }
40873         if(c.editor && c.editor.isFormField){
40874             c.editor = new Roo.grid.GridEditor(c.editor);
40875         }
40876         this.lookup[c.id] = c;
40877     }
40878     
40879 });
40880
40881 Roo.grid.ColumnModel.defaultRenderer = function(value)
40882 {
40883     if(typeof value == "object") {
40884         return value;
40885     }
40886         if(typeof value == "string" && value.length < 1){
40887             return "&#160;";
40888         }
40889     
40890         return String.format("{0}", value);
40891 };
40892
40893 // Alias for backwards compatibility
40894 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40895 /*
40896  * Based on:
40897  * Ext JS Library 1.1.1
40898  * Copyright(c) 2006-2007, Ext JS, LLC.
40899  *
40900  * Originally Released Under LGPL - original licence link has changed is not relivant.
40901  *
40902  * Fork - LGPL
40903  * <script type="text/javascript">
40904  */
40905
40906 /**
40907  * @class Roo.grid.AbstractSelectionModel
40908  * @extends Roo.util.Observable
40909  * @abstract
40910  * Abstract base class for grid SelectionModels.  It provides the interface that should be
40911  * implemented by descendant classes.  This class should not be directly instantiated.
40912  * @constructor
40913  */
40914 Roo.grid.AbstractSelectionModel = function(){
40915     this.locked = false;
40916     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40917 };
40918
40919 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
40920     /** @ignore Called by the grid automatically. Do not call directly. */
40921     init : function(grid){
40922         this.grid = grid;
40923         this.initEvents();
40924     },
40925
40926     /**
40927      * Locks the selections.
40928      */
40929     lock : function(){
40930         this.locked = true;
40931     },
40932
40933     /**
40934      * Unlocks the selections.
40935      */
40936     unlock : function(){
40937         this.locked = false;
40938     },
40939
40940     /**
40941      * Returns true if the selections are locked.
40942      * @return {Boolean}
40943      */
40944     isLocked : function(){
40945         return this.locked;
40946     }
40947 });/*
40948  * Based on:
40949  * Ext JS Library 1.1.1
40950  * Copyright(c) 2006-2007, Ext JS, LLC.
40951  *
40952  * Originally Released Under LGPL - original licence link has changed is not relivant.
40953  *
40954  * Fork - LGPL
40955  * <script type="text/javascript">
40956  */
40957 /**
40958  * @extends Roo.grid.AbstractSelectionModel
40959  * @class Roo.grid.RowSelectionModel
40960  * The default SelectionModel used by {@link Roo.grid.Grid}.
40961  * It supports multiple selections and keyboard selection/navigation. 
40962  * @constructor
40963  * @param {Object} config
40964  */
40965 Roo.grid.RowSelectionModel = function(config){
40966     Roo.apply(this, config);
40967     this.selections = new Roo.util.MixedCollection(false, function(o){
40968         return o.id;
40969     });
40970
40971     this.last = false;
40972     this.lastActive = false;
40973
40974     this.addEvents({
40975         /**
40976         * @event selectionchange
40977         * Fires when the selection changes
40978         * @param {SelectionModel} this
40979         */
40980        "selectionchange" : true,
40981        /**
40982         * @event afterselectionchange
40983         * Fires after the selection changes (eg. by key press or clicking)
40984         * @param {SelectionModel} this
40985         */
40986        "afterselectionchange" : true,
40987        /**
40988         * @event beforerowselect
40989         * Fires when a row is selected being selected, return false to cancel.
40990         * @param {SelectionModel} this
40991         * @param {Number} rowIndex The selected index
40992         * @param {Boolean} keepExisting False if other selections will be cleared
40993         */
40994        "beforerowselect" : true,
40995        /**
40996         * @event rowselect
40997         * Fires when a row is selected.
40998         * @param {SelectionModel} this
40999         * @param {Number} rowIndex The selected index
41000         * @param {Roo.data.Record} r The record
41001         */
41002        "rowselect" : true,
41003        /**
41004         * @event rowdeselect
41005         * Fires when a row is deselected.
41006         * @param {SelectionModel} this
41007         * @param {Number} rowIndex The selected index
41008         */
41009         "rowdeselect" : true
41010     });
41011     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
41012     this.locked = false;
41013 };
41014
41015 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
41016     /**
41017      * @cfg {Boolean} singleSelect
41018      * True to allow selection of only one row at a time (defaults to false)
41019      */
41020     singleSelect : false,
41021
41022     // private
41023     initEvents : function(){
41024
41025         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
41026             this.grid.on("mousedown", this.handleMouseDown, this);
41027         }else{ // allow click to work like normal
41028             this.grid.on("rowclick", this.handleDragableRowClick, this);
41029         }
41030         // bootstrap does not have a view..
41031         var view = this.grid.view ? this.grid.view : this.grid;
41032         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
41033             "up" : function(e){
41034                 if(!e.shiftKey){
41035                     this.selectPrevious(e.shiftKey);
41036                 }else if(this.last !== false && this.lastActive !== false){
41037                     var last = this.last;
41038                     this.selectRange(this.last,  this.lastActive-1);
41039                     view.focusRow(this.lastActive);
41040                     if(last !== false){
41041                         this.last = last;
41042                     }
41043                 }else{
41044                     this.selectFirstRow();
41045                 }
41046                 this.fireEvent("afterselectionchange", this);
41047             },
41048             "down" : function(e){
41049                 if(!e.shiftKey){
41050                     this.selectNext(e.shiftKey);
41051                 }else if(this.last !== false && this.lastActive !== false){
41052                     var last = this.last;
41053                     this.selectRange(this.last,  this.lastActive+1);
41054                     view.focusRow(this.lastActive);
41055                     if(last !== false){
41056                         this.last = last;
41057                     }
41058                 }else{
41059                     this.selectFirstRow();
41060                 }
41061                 this.fireEvent("afterselectionchange", this);
41062             },
41063             scope: this
41064         });
41065
41066          
41067         view.on("refresh", this.onRefresh, this);
41068         view.on("rowupdated", this.onRowUpdated, this);
41069         view.on("rowremoved", this.onRemove, this);
41070     },
41071
41072     // private
41073     onRefresh : function(){
41074         var ds = this.grid.ds, i, v = this.grid.view;
41075         var s = this.selections;
41076         s.each(function(r){
41077             if((i = ds.indexOfId(r.id)) != -1){
41078                 v.onRowSelect(i);
41079                 s.add(ds.getAt(i)); // updating the selection relate data
41080             }else{
41081                 s.remove(r);
41082             }
41083         });
41084     },
41085
41086     // private
41087     onRemove : function(v, index, r){
41088         this.selections.remove(r);
41089     },
41090
41091     // private
41092     onRowUpdated : function(v, index, r){
41093         if(this.isSelected(r)){
41094             v.onRowSelect(index);
41095         }
41096     },
41097
41098     /**
41099      * Select records.
41100      * @param {Array} records The records to select
41101      * @param {Boolean} keepExisting (optional) True to keep existing selections
41102      */
41103     selectRecords : function(records, keepExisting){
41104         if(!keepExisting){
41105             this.clearSelections();
41106         }
41107         var ds = this.grid.ds;
41108         for(var i = 0, len = records.length; i < len; i++){
41109             this.selectRow(ds.indexOf(records[i]), true);
41110         }
41111     },
41112
41113     /**
41114      * Gets the number of selected rows.
41115      * @return {Number}
41116      */
41117     getCount : function(){
41118         return this.selections.length;
41119     },
41120
41121     /**
41122      * Selects the first row in the grid.
41123      */
41124     selectFirstRow : function(){
41125         this.selectRow(0);
41126     },
41127
41128     /**
41129      * Select the last row.
41130      * @param {Boolean} keepExisting (optional) True to keep existing selections
41131      */
41132     selectLastRow : function(keepExisting){
41133         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
41134     },
41135
41136     /**
41137      * Selects the row immediately following the last selected row.
41138      * @param {Boolean} keepExisting (optional) True to keep existing selections
41139      */
41140     selectNext : function(keepExisting){
41141         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
41142             this.selectRow(this.last+1, keepExisting);
41143             var view = this.grid.view ? this.grid.view : this.grid;
41144             view.focusRow(this.last);
41145         }
41146     },
41147
41148     /**
41149      * Selects the row that precedes the last selected row.
41150      * @param {Boolean} keepExisting (optional) True to keep existing selections
41151      */
41152     selectPrevious : function(keepExisting){
41153         if(this.last){
41154             this.selectRow(this.last-1, keepExisting);
41155             var view = this.grid.view ? this.grid.view : this.grid;
41156             view.focusRow(this.last);
41157         }
41158     },
41159
41160     /**
41161      * Returns the selected records
41162      * @return {Array} Array of selected records
41163      */
41164     getSelections : function(){
41165         return [].concat(this.selections.items);
41166     },
41167
41168     /**
41169      * Returns the first selected record.
41170      * @return {Record}
41171      */
41172     getSelected : function(){
41173         return this.selections.itemAt(0);
41174     },
41175
41176
41177     /**
41178      * Clears all selections.
41179      */
41180     clearSelections : function(fast){
41181         if(this.locked) {
41182             return;
41183         }
41184         if(fast !== true){
41185             var ds = this.grid.ds;
41186             var s = this.selections;
41187             s.each(function(r){
41188                 this.deselectRow(ds.indexOfId(r.id));
41189             }, this);
41190             s.clear();
41191         }else{
41192             this.selections.clear();
41193         }
41194         this.last = false;
41195     },
41196
41197
41198     /**
41199      * Selects all rows.
41200      */
41201     selectAll : function(){
41202         if(this.locked) {
41203             return;
41204         }
41205         this.selections.clear();
41206         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
41207             this.selectRow(i, true);
41208         }
41209     },
41210
41211     /**
41212      * Returns True if there is a selection.
41213      * @return {Boolean}
41214      */
41215     hasSelection : function(){
41216         return this.selections.length > 0;
41217     },
41218
41219     /**
41220      * Returns True if the specified row is selected.
41221      * @param {Number/Record} record The record or index of the record to check
41222      * @return {Boolean}
41223      */
41224     isSelected : function(index){
41225         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
41226         return (r && this.selections.key(r.id) ? true : false);
41227     },
41228
41229     /**
41230      * Returns True if the specified record id is selected.
41231      * @param {String} id The id of record to check
41232      * @return {Boolean}
41233      */
41234     isIdSelected : function(id){
41235         return (this.selections.key(id) ? true : false);
41236     },
41237
41238     // private
41239     handleMouseDown : function(e, t)
41240     {
41241         var view = this.grid.view ? this.grid.view : this.grid;
41242         var rowIndex;
41243         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
41244             return;
41245         };
41246         if(e.shiftKey && this.last !== false){
41247             var last = this.last;
41248             this.selectRange(last, rowIndex, e.ctrlKey);
41249             this.last = last; // reset the last
41250             view.focusRow(rowIndex);
41251         }else{
41252             var isSelected = this.isSelected(rowIndex);
41253             if(e.button !== 0 && isSelected){
41254                 view.focusRow(rowIndex);
41255             }else if(e.ctrlKey && isSelected){
41256                 this.deselectRow(rowIndex);
41257             }else if(!isSelected){
41258                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
41259                 view.focusRow(rowIndex);
41260             }
41261         }
41262         this.fireEvent("afterselectionchange", this);
41263     },
41264     // private
41265     handleDragableRowClick :  function(grid, rowIndex, e) 
41266     {
41267         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
41268             this.selectRow(rowIndex, false);
41269             var view = this.grid.view ? this.grid.view : this.grid;
41270             view.focusRow(rowIndex);
41271              this.fireEvent("afterselectionchange", this);
41272         }
41273     },
41274     
41275     /**
41276      * Selects multiple rows.
41277      * @param {Array} rows Array of the indexes of the row to select
41278      * @param {Boolean} keepExisting (optional) True to keep existing selections
41279      */
41280     selectRows : function(rows, keepExisting){
41281         if(!keepExisting){
41282             this.clearSelections();
41283         }
41284         for(var i = 0, len = rows.length; i < len; i++){
41285             this.selectRow(rows[i], true);
41286         }
41287     },
41288
41289     /**
41290      * Selects a range of rows. All rows in between startRow and endRow are also selected.
41291      * @param {Number} startRow The index of the first row in the range
41292      * @param {Number} endRow The index of the last row in the range
41293      * @param {Boolean} keepExisting (optional) True to retain existing selections
41294      */
41295     selectRange : function(startRow, endRow, keepExisting){
41296         if(this.locked) {
41297             return;
41298         }
41299         if(!keepExisting){
41300             this.clearSelections();
41301         }
41302         if(startRow <= endRow){
41303             for(var i = startRow; i <= endRow; i++){
41304                 this.selectRow(i, true);
41305             }
41306         }else{
41307             for(var i = startRow; i >= endRow; i--){
41308                 this.selectRow(i, true);
41309             }
41310         }
41311     },
41312
41313     /**
41314      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
41315      * @param {Number} startRow The index of the first row in the range
41316      * @param {Number} endRow The index of the last row in the range
41317      */
41318     deselectRange : function(startRow, endRow, preventViewNotify){
41319         if(this.locked) {
41320             return;
41321         }
41322         for(var i = startRow; i <= endRow; i++){
41323             this.deselectRow(i, preventViewNotify);
41324         }
41325     },
41326
41327     /**
41328      * Selects a row.
41329      * @param {Number} row The index of the row to select
41330      * @param {Boolean} keepExisting (optional) True to keep existing selections
41331      */
41332     selectRow : function(index, keepExisting, preventViewNotify){
41333         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
41334             return;
41335         }
41336         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
41337             if(!keepExisting || this.singleSelect){
41338                 this.clearSelections();
41339             }
41340             var r = this.grid.ds.getAt(index);
41341             this.selections.add(r);
41342             this.last = this.lastActive = index;
41343             if(!preventViewNotify){
41344                 var view = this.grid.view ? this.grid.view : this.grid;
41345                 view.onRowSelect(index);
41346             }
41347             this.fireEvent("rowselect", this, index, r);
41348             this.fireEvent("selectionchange", this);
41349         }
41350     },
41351
41352     /**
41353      * Deselects a row.
41354      * @param {Number} row The index of the row to deselect
41355      */
41356     deselectRow : function(index, preventViewNotify){
41357         if(this.locked) {
41358             return;
41359         }
41360         if(this.last == index){
41361             this.last = false;
41362         }
41363         if(this.lastActive == index){
41364             this.lastActive = false;
41365         }
41366         var r = this.grid.ds.getAt(index);
41367         this.selections.remove(r);
41368         if(!preventViewNotify){
41369             var view = this.grid.view ? this.grid.view : this.grid;
41370             view.onRowDeselect(index);
41371         }
41372         this.fireEvent("rowdeselect", this, index);
41373         this.fireEvent("selectionchange", this);
41374     },
41375
41376     // private
41377     restoreLast : function(){
41378         if(this._last){
41379             this.last = this._last;
41380         }
41381     },
41382
41383     // private
41384     acceptsNav : function(row, col, cm){
41385         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41386     },
41387
41388     // private
41389     onEditorKey : function(field, e){
41390         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
41391         if(k == e.TAB){
41392             e.stopEvent();
41393             ed.completeEdit();
41394             if(e.shiftKey){
41395                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41396             }else{
41397                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41398             }
41399         }else if(k == e.ENTER && !e.ctrlKey){
41400             e.stopEvent();
41401             ed.completeEdit();
41402             if(e.shiftKey){
41403                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
41404             }else{
41405                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
41406             }
41407         }else if(k == e.ESC){
41408             ed.cancelEdit();
41409         }
41410         if(newCell){
41411             g.startEditing(newCell[0], newCell[1]);
41412         }
41413     }
41414 });/*
41415  * Based on:
41416  * Ext JS Library 1.1.1
41417  * Copyright(c) 2006-2007, Ext JS, LLC.
41418  *
41419  * Originally Released Under LGPL - original licence link has changed is not relivant.
41420  *
41421  * Fork - LGPL
41422  * <script type="text/javascript">
41423  */
41424 /**
41425  * @class Roo.grid.CellSelectionModel
41426  * @extends Roo.grid.AbstractSelectionModel
41427  * This class provides the basic implementation for cell selection in a grid.
41428  * @constructor
41429  * @param {Object} config The object containing the configuration of this model.
41430  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
41431  */
41432 Roo.grid.CellSelectionModel = function(config){
41433     Roo.apply(this, config);
41434
41435     this.selection = null;
41436
41437     this.addEvents({
41438         /**
41439              * @event beforerowselect
41440              * Fires before a cell is selected.
41441              * @param {SelectionModel} this
41442              * @param {Number} rowIndex The selected row index
41443              * @param {Number} colIndex The selected cell index
41444              */
41445             "beforecellselect" : true,
41446         /**
41447              * @event cellselect
41448              * Fires when a cell is selected.
41449              * @param {SelectionModel} this
41450              * @param {Number} rowIndex The selected row index
41451              * @param {Number} colIndex The selected cell index
41452              */
41453             "cellselect" : true,
41454         /**
41455              * @event selectionchange
41456              * Fires when the active selection changes.
41457              * @param {SelectionModel} this
41458              * @param {Object} selection null for no selection or an object (o) with two properties
41459                 <ul>
41460                 <li>o.record: the record object for the row the selection is in</li>
41461                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
41462                 </ul>
41463              */
41464             "selectionchange" : true,
41465         /**
41466              * @event tabend
41467              * Fires when the tab (or enter) was pressed on the last editable cell
41468              * You can use this to trigger add new row.
41469              * @param {SelectionModel} this
41470              */
41471             "tabend" : true,
41472          /**
41473              * @event beforeeditnext
41474              * Fires before the next editable sell is made active
41475              * You can use this to skip to another cell or fire the tabend
41476              *    if you set cell to false
41477              * @param {Object} eventdata object : { cell : [ row, col ] } 
41478              */
41479             "beforeeditnext" : true
41480     });
41481     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
41482 };
41483
41484 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
41485     
41486     enter_is_tab: false,
41487
41488     /** @ignore */
41489     initEvents : function(){
41490         this.grid.on("mousedown", this.handleMouseDown, this);
41491         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
41492         var view = this.grid.view;
41493         view.on("refresh", this.onViewChange, this);
41494         view.on("rowupdated", this.onRowUpdated, this);
41495         view.on("beforerowremoved", this.clearSelections, this);
41496         view.on("beforerowsinserted", this.clearSelections, this);
41497         if(this.grid.isEditor){
41498             this.grid.on("beforeedit", this.beforeEdit,  this);
41499         }
41500     },
41501
41502         //private
41503     beforeEdit : function(e){
41504         this.select(e.row, e.column, false, true, e.record);
41505     },
41506
41507         //private
41508     onRowUpdated : function(v, index, r){
41509         if(this.selection && this.selection.record == r){
41510             v.onCellSelect(index, this.selection.cell[1]);
41511         }
41512     },
41513
41514         //private
41515     onViewChange : function(){
41516         this.clearSelections(true);
41517     },
41518
41519         /**
41520          * Returns the currently selected cell,.
41521          * @return {Array} The selected cell (row, column) or null if none selected.
41522          */
41523     getSelectedCell : function(){
41524         return this.selection ? this.selection.cell : null;
41525     },
41526
41527     /**
41528      * Clears all selections.
41529      * @param {Boolean} true to prevent the gridview from being notified about the change.
41530      */
41531     clearSelections : function(preventNotify){
41532         var s = this.selection;
41533         if(s){
41534             if(preventNotify !== true){
41535                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
41536             }
41537             this.selection = null;
41538             this.fireEvent("selectionchange", this, null);
41539         }
41540     },
41541
41542     /**
41543      * Returns true if there is a selection.
41544      * @return {Boolean}
41545      */
41546     hasSelection : function(){
41547         return this.selection ? true : false;
41548     },
41549
41550     /** @ignore */
41551     handleMouseDown : function(e, t){
41552         var v = this.grid.getView();
41553         if(this.isLocked()){
41554             return;
41555         };
41556         var row = v.findRowIndex(t);
41557         var cell = v.findCellIndex(t);
41558         if(row !== false && cell !== false){
41559             this.select(row, cell);
41560         }
41561     },
41562
41563     /**
41564      * Selects a cell.
41565      * @param {Number} rowIndex
41566      * @param {Number} collIndex
41567      */
41568     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
41569         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
41570             this.clearSelections();
41571             r = r || this.grid.dataSource.getAt(rowIndex);
41572             this.selection = {
41573                 record : r,
41574                 cell : [rowIndex, colIndex]
41575             };
41576             if(!preventViewNotify){
41577                 var v = this.grid.getView();
41578                 v.onCellSelect(rowIndex, colIndex);
41579                 if(preventFocus !== true){
41580                     v.focusCell(rowIndex, colIndex);
41581                 }
41582             }
41583             this.fireEvent("cellselect", this, rowIndex, colIndex);
41584             this.fireEvent("selectionchange", this, this.selection);
41585         }
41586     },
41587
41588         //private
41589     isSelectable : function(rowIndex, colIndex, cm){
41590         return !cm.isHidden(colIndex);
41591     },
41592
41593     /** @ignore */
41594     handleKeyDown : function(e){
41595         //Roo.log('Cell Sel Model handleKeyDown');
41596         if(!e.isNavKeyPress()){
41597             return;
41598         }
41599         var g = this.grid, s = this.selection;
41600         if(!s){
41601             e.stopEvent();
41602             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
41603             if(cell){
41604                 this.select(cell[0], cell[1]);
41605             }
41606             return;
41607         }
41608         var sm = this;
41609         var walk = function(row, col, step){
41610             return g.walkCells(row, col, step, sm.isSelectable,  sm);
41611         };
41612         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
41613         var newCell;
41614
41615       
41616
41617         switch(k){
41618             case e.TAB:
41619                 // handled by onEditorKey
41620                 if (g.isEditor && g.editing) {
41621                     return;
41622                 }
41623                 if(e.shiftKey) {
41624                     newCell = walk(r, c-1, -1);
41625                 } else {
41626                     newCell = walk(r, c+1, 1);
41627                 }
41628                 break;
41629             
41630             case e.DOWN:
41631                newCell = walk(r+1, c, 1);
41632                 break;
41633             
41634             case e.UP:
41635                 newCell = walk(r-1, c, -1);
41636                 break;
41637             
41638             case e.RIGHT:
41639                 newCell = walk(r, c+1, 1);
41640                 break;
41641             
41642             case e.LEFT:
41643                 newCell = walk(r, c-1, -1);
41644                 break;
41645             
41646             case e.ENTER:
41647                 
41648                 if(g.isEditor && !g.editing){
41649                    g.startEditing(r, c);
41650                    e.stopEvent();
41651                    return;
41652                 }
41653                 
41654                 
41655              break;
41656         };
41657         if(newCell){
41658             this.select(newCell[0], newCell[1]);
41659             e.stopEvent();
41660             
41661         }
41662     },
41663
41664     acceptsNav : function(row, col, cm){
41665         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41666     },
41667     /**
41668      * Selects a cell.
41669      * @param {Number} field (not used) - as it's normally used as a listener
41670      * @param {Number} e - event - fake it by using
41671      *
41672      * var e = Roo.EventObjectImpl.prototype;
41673      * e.keyCode = e.TAB
41674      *
41675      * 
41676      */
41677     onEditorKey : function(field, e){
41678         
41679         var k = e.getKey(),
41680             newCell,
41681             g = this.grid,
41682             ed = g.activeEditor,
41683             forward = false;
41684         ///Roo.log('onEditorKey' + k);
41685         
41686         
41687         if (this.enter_is_tab && k == e.ENTER) {
41688             k = e.TAB;
41689         }
41690         
41691         if(k == e.TAB){
41692             if(e.shiftKey){
41693                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41694             }else{
41695                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41696                 forward = true;
41697             }
41698             
41699             e.stopEvent();
41700             
41701         } else if(k == e.ENTER &&  !e.ctrlKey){
41702             ed.completeEdit();
41703             e.stopEvent();
41704             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41705         
41706                 } else if(k == e.ESC){
41707             ed.cancelEdit();
41708         }
41709                 
41710         if (newCell) {
41711             var ecall = { cell : newCell, forward : forward };
41712             this.fireEvent('beforeeditnext', ecall );
41713             newCell = ecall.cell;
41714                         forward = ecall.forward;
41715         }
41716                 
41717         if(newCell){
41718             //Roo.log('next cell after edit');
41719             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41720         } else if (forward) {
41721             // tabbed past last
41722             this.fireEvent.defer(100, this, ['tabend',this]);
41723         }
41724     }
41725 });/*
41726  * Based on:
41727  * Ext JS Library 1.1.1
41728  * Copyright(c) 2006-2007, Ext JS, LLC.
41729  *
41730  * Originally Released Under LGPL - original licence link has changed is not relivant.
41731  *
41732  * Fork - LGPL
41733  * <script type="text/javascript">
41734  */
41735  
41736 /**
41737  * @class Roo.grid.EditorGrid
41738  * @extends Roo.grid.Grid
41739  * Class for creating and editable grid.
41740  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
41741  * The container MUST have some type of size defined for the grid to fill. The container will be 
41742  * automatically set to position relative if it isn't already.
41743  * @param {Object} dataSource The data model to bind to
41744  * @param {Object} colModel The column model with info about this grid's columns
41745  */
41746 Roo.grid.EditorGrid = function(container, config){
41747     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41748     this.getGridEl().addClass("xedit-grid");
41749
41750     if(!this.selModel){
41751         this.selModel = new Roo.grid.CellSelectionModel();
41752     }
41753
41754     this.activeEditor = null;
41755
41756         this.addEvents({
41757             /**
41758              * @event beforeedit
41759              * Fires before cell editing is triggered. The edit event object has the following properties <br />
41760              * <ul style="padding:5px;padding-left:16px;">
41761              * <li>grid - This grid</li>
41762              * <li>record - The record being edited</li>
41763              * <li>field - The field name being edited</li>
41764              * <li>value - The value for the field being edited.</li>
41765              * <li>row - The grid row index</li>
41766              * <li>column - The grid column index</li>
41767              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41768              * </ul>
41769              * @param {Object} e An edit event (see above for description)
41770              */
41771             "beforeedit" : true,
41772             /**
41773              * @event afteredit
41774              * Fires after a cell is edited. <br />
41775              * <ul style="padding:5px;padding-left:16px;">
41776              * <li>grid - This grid</li>
41777              * <li>record - The record being edited</li>
41778              * <li>field - The field name being edited</li>
41779              * <li>value - The value being set</li>
41780              * <li>originalValue - The original value for the field, before the edit.</li>
41781              * <li>row - The grid row index</li>
41782              * <li>column - The grid column index</li>
41783              * </ul>
41784              * @param {Object} e An edit event (see above for description)
41785              */
41786             "afteredit" : true,
41787             /**
41788              * @event validateedit
41789              * Fires after a cell is edited, but before the value is set in the record. 
41790          * You can use this to modify the value being set in the field, Return false
41791              * to cancel the change. The edit event object has the following properties <br />
41792              * <ul style="padding:5px;padding-left:16px;">
41793          * <li>editor - This editor</li>
41794              * <li>grid - This grid</li>
41795              * <li>record - The record being edited</li>
41796              * <li>field - The field name being edited</li>
41797              * <li>value - The value being set</li>
41798              * <li>originalValue - The original value for the field, before the edit.</li>
41799              * <li>row - The grid row index</li>
41800              * <li>column - The grid column index</li>
41801              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41802              * </ul>
41803              * @param {Object} e An edit event (see above for description)
41804              */
41805             "validateedit" : true
41806         });
41807     this.on("bodyscroll", this.stopEditing,  this);
41808     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
41809 };
41810
41811 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41812     /**
41813      * @cfg {Number} clicksToEdit
41814      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41815      */
41816     clicksToEdit: 2,
41817
41818     // private
41819     isEditor : true,
41820     // private
41821     trackMouseOver: false, // causes very odd FF errors
41822
41823     onCellDblClick : function(g, row, col){
41824         this.startEditing(row, col);
41825     },
41826
41827     onEditComplete : function(ed, value, startValue){
41828         this.editing = false;
41829         this.activeEditor = null;
41830         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41831         var r = ed.record;
41832         var field = this.colModel.getDataIndex(ed.col);
41833         var e = {
41834             grid: this,
41835             record: r,
41836             field: field,
41837             originalValue: startValue,
41838             value: value,
41839             row: ed.row,
41840             column: ed.col,
41841             cancel:false,
41842             editor: ed
41843         };
41844         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41845         cell.show();
41846           
41847         if(String(value) !== String(startValue)){
41848             
41849             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41850                 r.set(field, e.value);
41851                 // if we are dealing with a combo box..
41852                 // then we also set the 'name' colum to be the displayField
41853                 if (ed.field.displayField && ed.field.name) {
41854                     r.set(ed.field.name, ed.field.el.dom.value);
41855                 }
41856                 
41857                 delete e.cancel; //?? why!!!
41858                 this.fireEvent("afteredit", e);
41859             }
41860         } else {
41861             this.fireEvent("afteredit", e); // always fire it!
41862         }
41863         this.view.focusCell(ed.row, ed.col);
41864     },
41865
41866     /**
41867      * Starts editing the specified for the specified row/column
41868      * @param {Number} rowIndex
41869      * @param {Number} colIndex
41870      */
41871     startEditing : function(row, col){
41872         this.stopEditing();
41873         if(this.colModel.isCellEditable(col, row)){
41874             this.view.ensureVisible(row, col, true);
41875           
41876             var r = this.dataSource.getAt(row);
41877             var field = this.colModel.getDataIndex(col);
41878             var cell = Roo.get(this.view.getCell(row,col));
41879             var e = {
41880                 grid: this,
41881                 record: r,
41882                 field: field,
41883                 value: r.data[field],
41884                 row: row,
41885                 column: col,
41886                 cancel:false 
41887             };
41888             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41889                 this.editing = true;
41890                 var ed = this.colModel.getCellEditor(col, row);
41891                 
41892                 if (!ed) {
41893                     return;
41894                 }
41895                 if(!ed.rendered){
41896                     ed.render(ed.parentEl || document.body);
41897                 }
41898                 ed.field.reset();
41899                
41900                 cell.hide();
41901                 
41902                 (function(){ // complex but required for focus issues in safari, ie and opera
41903                     ed.row = row;
41904                     ed.col = col;
41905                     ed.record = r;
41906                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
41907                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
41908                     this.activeEditor = ed;
41909                     var v = r.data[field];
41910                     ed.startEdit(this.view.getCell(row, col), v);
41911                     // combo's with 'displayField and name set
41912                     if (ed.field.displayField && ed.field.name) {
41913                         ed.field.el.dom.value = r.data[ed.field.name];
41914                     }
41915                     
41916                     
41917                 }).defer(50, this);
41918             }
41919         }
41920     },
41921         
41922     /**
41923      * Stops any active editing
41924      */
41925     stopEditing : function(){
41926         if(this.activeEditor){
41927             this.activeEditor.completeEdit();
41928         }
41929         this.activeEditor = null;
41930     },
41931         
41932          /**
41933      * Called to get grid's drag proxy text, by default returns this.ddText.
41934      * @return {String}
41935      */
41936     getDragDropText : function(){
41937         var count = this.selModel.getSelectedCell() ? 1 : 0;
41938         return String.format(this.ddText, count, count == 1 ? '' : 's');
41939     }
41940         
41941 });/*
41942  * Based on:
41943  * Ext JS Library 1.1.1
41944  * Copyright(c) 2006-2007, Ext JS, LLC.
41945  *
41946  * Originally Released Under LGPL - original licence link has changed is not relivant.
41947  *
41948  * Fork - LGPL
41949  * <script type="text/javascript">
41950  */
41951
41952 // private - not really -- you end up using it !
41953 // This is a support class used internally by the Grid components
41954
41955 /**
41956  * @class Roo.grid.GridEditor
41957  * @extends Roo.Editor
41958  * Class for creating and editable grid elements.
41959  * @param {Object} config any settings (must include field)
41960  */
41961 Roo.grid.GridEditor = function(field, config){
41962     if (!config && field.field) {
41963         config = field;
41964         field = Roo.factory(config.field, Roo.form);
41965     }
41966     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41967     field.monitorTab = false;
41968 };
41969
41970 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41971     
41972     /**
41973      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41974      */
41975     
41976     alignment: "tl-tl",
41977     autoSize: "width",
41978     hideEl : false,
41979     cls: "x-small-editor x-grid-editor",
41980     shim:false,
41981     shadow:"frame"
41982 });/*
41983  * Based on:
41984  * Ext JS Library 1.1.1
41985  * Copyright(c) 2006-2007, Ext JS, LLC.
41986  *
41987  * Originally Released Under LGPL - original licence link has changed is not relivant.
41988  *
41989  * Fork - LGPL
41990  * <script type="text/javascript">
41991  */
41992   
41993
41994   
41995 Roo.grid.PropertyRecord = Roo.data.Record.create([
41996     {name:'name',type:'string'},  'value'
41997 ]);
41998
41999
42000 Roo.grid.PropertyStore = function(grid, source){
42001     this.grid = grid;
42002     this.store = new Roo.data.Store({
42003         recordType : Roo.grid.PropertyRecord
42004     });
42005     this.store.on('update', this.onUpdate,  this);
42006     if(source){
42007         this.setSource(source);
42008     }
42009     Roo.grid.PropertyStore.superclass.constructor.call(this);
42010 };
42011
42012
42013
42014 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
42015     setSource : function(o){
42016         this.source = o;
42017         this.store.removeAll();
42018         var data = [];
42019         for(var k in o){
42020             if(this.isEditableValue(o[k])){
42021                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
42022             }
42023         }
42024         this.store.loadRecords({records: data}, {}, true);
42025     },
42026
42027     onUpdate : function(ds, record, type){
42028         if(type == Roo.data.Record.EDIT){
42029             var v = record.data['value'];
42030             var oldValue = record.modified['value'];
42031             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
42032                 this.source[record.id] = v;
42033                 record.commit();
42034                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
42035             }else{
42036                 record.reject();
42037             }
42038         }
42039     },
42040
42041     getProperty : function(row){
42042        return this.store.getAt(row);
42043     },
42044
42045     isEditableValue: function(val){
42046         if(val && val instanceof Date){
42047             return true;
42048         }else if(typeof val == 'object' || typeof val == 'function'){
42049             return false;
42050         }
42051         return true;
42052     },
42053
42054     setValue : function(prop, value){
42055         this.source[prop] = value;
42056         this.store.getById(prop).set('value', value);
42057     },
42058
42059     getSource : function(){
42060         return this.source;
42061     }
42062 });
42063
42064 Roo.grid.PropertyColumnModel = function(grid, store){
42065     this.grid = grid;
42066     var g = Roo.grid;
42067     g.PropertyColumnModel.superclass.constructor.call(this, [
42068         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
42069         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
42070     ]);
42071     this.store = store;
42072     this.bselect = Roo.DomHelper.append(document.body, {
42073         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
42074             {tag: 'option', value: 'true', html: 'true'},
42075             {tag: 'option', value: 'false', html: 'false'}
42076         ]
42077     });
42078     Roo.id(this.bselect);
42079     var f = Roo.form;
42080     this.editors = {
42081         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
42082         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
42083         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
42084         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
42085         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
42086     };
42087     this.renderCellDelegate = this.renderCell.createDelegate(this);
42088     this.renderPropDelegate = this.renderProp.createDelegate(this);
42089 };
42090
42091 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
42092     
42093     
42094     nameText : 'Name',
42095     valueText : 'Value',
42096     
42097     dateFormat : 'm/j/Y',
42098     
42099     
42100     renderDate : function(dateVal){
42101         return dateVal.dateFormat(this.dateFormat);
42102     },
42103
42104     renderBool : function(bVal){
42105         return bVal ? 'true' : 'false';
42106     },
42107
42108     isCellEditable : function(colIndex, rowIndex){
42109         return colIndex == 1;
42110     },
42111
42112     getRenderer : function(col){
42113         return col == 1 ?
42114             this.renderCellDelegate : this.renderPropDelegate;
42115     },
42116
42117     renderProp : function(v){
42118         return this.getPropertyName(v);
42119     },
42120
42121     renderCell : function(val){
42122         var rv = val;
42123         if(val instanceof Date){
42124             rv = this.renderDate(val);
42125         }else if(typeof val == 'boolean'){
42126             rv = this.renderBool(val);
42127         }
42128         return Roo.util.Format.htmlEncode(rv);
42129     },
42130
42131     getPropertyName : function(name){
42132         var pn = this.grid.propertyNames;
42133         return pn && pn[name] ? pn[name] : name;
42134     },
42135
42136     getCellEditor : function(colIndex, rowIndex){
42137         var p = this.store.getProperty(rowIndex);
42138         var n = p.data['name'], val = p.data['value'];
42139         
42140         if(typeof(this.grid.customEditors[n]) == 'string'){
42141             return this.editors[this.grid.customEditors[n]];
42142         }
42143         if(typeof(this.grid.customEditors[n]) != 'undefined'){
42144             return this.grid.customEditors[n];
42145         }
42146         if(val instanceof Date){
42147             return this.editors['date'];
42148         }else if(typeof val == 'number'){
42149             return this.editors['number'];
42150         }else if(typeof val == 'boolean'){
42151             return this.editors['boolean'];
42152         }else{
42153             return this.editors['string'];
42154         }
42155     }
42156 });
42157
42158 /**
42159  * @class Roo.grid.PropertyGrid
42160  * @extends Roo.grid.EditorGrid
42161  * This class represents the  interface of a component based property grid control.
42162  * <br><br>Usage:<pre><code>
42163  var grid = new Roo.grid.PropertyGrid("my-container-id", {
42164       
42165  });
42166  // set any options
42167  grid.render();
42168  * </code></pre>
42169   
42170  * @constructor
42171  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
42172  * The container MUST have some type of size defined for the grid to fill. The container will be
42173  * automatically set to position relative if it isn't already.
42174  * @param {Object} config A config object that sets properties on this grid.
42175  */
42176 Roo.grid.PropertyGrid = function(container, config){
42177     config = config || {};
42178     var store = new Roo.grid.PropertyStore(this);
42179     this.store = store;
42180     var cm = new Roo.grid.PropertyColumnModel(this, store);
42181     store.store.sort('name', 'ASC');
42182     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
42183         ds: store.store,
42184         cm: cm,
42185         enableColLock:false,
42186         enableColumnMove:false,
42187         stripeRows:false,
42188         trackMouseOver: false,
42189         clicksToEdit:1
42190     }, config));
42191     this.getGridEl().addClass('x-props-grid');
42192     this.lastEditRow = null;
42193     this.on('columnresize', this.onColumnResize, this);
42194     this.addEvents({
42195          /**
42196              * @event beforepropertychange
42197              * Fires before a property changes (return false to stop?)
42198              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
42199              * @param {String} id Record Id
42200              * @param {String} newval New Value
42201          * @param {String} oldval Old Value
42202              */
42203         "beforepropertychange": true,
42204         /**
42205              * @event propertychange
42206              * Fires after a property changes
42207              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
42208              * @param {String} id Record Id
42209              * @param {String} newval New Value
42210          * @param {String} oldval Old Value
42211              */
42212         "propertychange": true
42213     });
42214     this.customEditors = this.customEditors || {};
42215 };
42216 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
42217     
42218      /**
42219      * @cfg {Object} customEditors map of colnames=> custom editors.
42220      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
42221      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
42222      * false disables editing of the field.
42223          */
42224     
42225       /**
42226      * @cfg {Object} propertyNames map of property Names to their displayed value
42227          */
42228     
42229     render : function(){
42230         Roo.grid.PropertyGrid.superclass.render.call(this);
42231         this.autoSize.defer(100, this);
42232     },
42233
42234     autoSize : function(){
42235         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
42236         if(this.view){
42237             this.view.fitColumns();
42238         }
42239     },
42240
42241     onColumnResize : function(){
42242         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
42243         this.autoSize();
42244     },
42245     /**
42246      * Sets the data for the Grid
42247      * accepts a Key => Value object of all the elements avaiable.
42248      * @param {Object} data  to appear in grid.
42249      */
42250     setSource : function(source){
42251         this.store.setSource(source);
42252         //this.autoSize();
42253     },
42254     /**
42255      * Gets all the data from the grid.
42256      * @return {Object} data  data stored in grid
42257      */
42258     getSource : function(){
42259         return this.store.getSource();
42260     }
42261 });/*
42262   
42263  * Licence LGPL
42264  
42265  */
42266  
42267 /**
42268  * @class Roo.grid.Calendar
42269  * @extends Roo.grid.Grid
42270  * This class extends the Grid to provide a calendar widget
42271  * <br><br>Usage:<pre><code>
42272  var grid = new Roo.grid.Calendar("my-container-id", {
42273      ds: myDataStore,
42274      cm: myColModel,
42275      selModel: mySelectionModel,
42276      autoSizeColumns: true,
42277      monitorWindowResize: false,
42278      trackMouseOver: true
42279      eventstore : real data store..
42280  });
42281  // set any options
42282  grid.render();
42283   
42284   * @constructor
42285  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
42286  * The container MUST have some type of size defined for the grid to fill. The container will be
42287  * automatically set to position relative if it isn't already.
42288  * @param {Object} config A config object that sets properties on this grid.
42289  */
42290 Roo.grid.Calendar = function(container, config){
42291         // initialize the container
42292         this.container = Roo.get(container);
42293         this.container.update("");
42294         this.container.setStyle("overflow", "hidden");
42295     this.container.addClass('x-grid-container');
42296
42297     this.id = this.container.id;
42298
42299     Roo.apply(this, config);
42300     // check and correct shorthanded configs
42301     
42302     var rows = [];
42303     var d =1;
42304     for (var r = 0;r < 6;r++) {
42305         
42306         rows[r]=[];
42307         for (var c =0;c < 7;c++) {
42308             rows[r][c]= '';
42309         }
42310     }
42311     if (this.eventStore) {
42312         this.eventStore= Roo.factory(this.eventStore, Roo.data);
42313         this.eventStore.on('load',this.onLoad, this);
42314         this.eventStore.on('beforeload',this.clearEvents, this);
42315          
42316     }
42317     
42318     this.dataSource = new Roo.data.Store({
42319             proxy: new Roo.data.MemoryProxy(rows),
42320             reader: new Roo.data.ArrayReader({}, [
42321                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
42322     });
42323
42324     this.dataSource.load();
42325     this.ds = this.dataSource;
42326     this.ds.xmodule = this.xmodule || false;
42327     
42328     
42329     var cellRender = function(v,x,r)
42330     {
42331         return String.format(
42332             '<div class="fc-day  fc-widget-content"><div>' +
42333                 '<div class="fc-event-container"></div>' +
42334                 '<div class="fc-day-number">{0}</div>'+
42335                 
42336                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
42337             '</div></div>', v);
42338     
42339     }
42340     
42341     
42342     this.colModel = new Roo.grid.ColumnModel( [
42343         {
42344             xtype: 'ColumnModel',
42345             xns: Roo.grid,
42346             dataIndex : 'weekday0',
42347             header : 'Sunday',
42348             renderer : cellRender
42349         },
42350         {
42351             xtype: 'ColumnModel',
42352             xns: Roo.grid,
42353             dataIndex : 'weekday1',
42354             header : 'Monday',
42355             renderer : cellRender
42356         },
42357         {
42358             xtype: 'ColumnModel',
42359             xns: Roo.grid,
42360             dataIndex : 'weekday2',
42361             header : 'Tuesday',
42362             renderer : cellRender
42363         },
42364         {
42365             xtype: 'ColumnModel',
42366             xns: Roo.grid,
42367             dataIndex : 'weekday3',
42368             header : 'Wednesday',
42369             renderer : cellRender
42370         },
42371         {
42372             xtype: 'ColumnModel',
42373             xns: Roo.grid,
42374             dataIndex : 'weekday4',
42375             header : 'Thursday',
42376             renderer : cellRender
42377         },
42378         {
42379             xtype: 'ColumnModel',
42380             xns: Roo.grid,
42381             dataIndex : 'weekday5',
42382             header : 'Friday',
42383             renderer : cellRender
42384         },
42385         {
42386             xtype: 'ColumnModel',
42387             xns: Roo.grid,
42388             dataIndex : 'weekday6',
42389             header : 'Saturday',
42390             renderer : cellRender
42391         }
42392     ]);
42393     this.cm = this.colModel;
42394     this.cm.xmodule = this.xmodule || false;
42395  
42396         
42397           
42398     //this.selModel = new Roo.grid.CellSelectionModel();
42399     //this.sm = this.selModel;
42400     //this.selModel.init(this);
42401     
42402     
42403     if(this.width){
42404         this.container.setWidth(this.width);
42405     }
42406
42407     if(this.height){
42408         this.container.setHeight(this.height);
42409     }
42410     /** @private */
42411         this.addEvents({
42412         // raw events
42413         /**
42414          * @event click
42415          * The raw click event for the entire grid.
42416          * @param {Roo.EventObject} e
42417          */
42418         "click" : true,
42419         /**
42420          * @event dblclick
42421          * The raw dblclick event for the entire grid.
42422          * @param {Roo.EventObject} e
42423          */
42424         "dblclick" : true,
42425         /**
42426          * @event contextmenu
42427          * The raw contextmenu event for the entire grid.
42428          * @param {Roo.EventObject} e
42429          */
42430         "contextmenu" : true,
42431         /**
42432          * @event mousedown
42433          * The raw mousedown event for the entire grid.
42434          * @param {Roo.EventObject} e
42435          */
42436         "mousedown" : true,
42437         /**
42438          * @event mouseup
42439          * The raw mouseup event for the entire grid.
42440          * @param {Roo.EventObject} e
42441          */
42442         "mouseup" : true,
42443         /**
42444          * @event mouseover
42445          * The raw mouseover event for the entire grid.
42446          * @param {Roo.EventObject} e
42447          */
42448         "mouseover" : true,
42449         /**
42450          * @event mouseout
42451          * The raw mouseout event for the entire grid.
42452          * @param {Roo.EventObject} e
42453          */
42454         "mouseout" : true,
42455         /**
42456          * @event keypress
42457          * The raw keypress event for the entire grid.
42458          * @param {Roo.EventObject} e
42459          */
42460         "keypress" : true,
42461         /**
42462          * @event keydown
42463          * The raw keydown event for the entire grid.
42464          * @param {Roo.EventObject} e
42465          */
42466         "keydown" : true,
42467
42468         // custom events
42469
42470         /**
42471          * @event cellclick
42472          * Fires when a cell is clicked
42473          * @param {Grid} this
42474          * @param {Number} rowIndex
42475          * @param {Number} columnIndex
42476          * @param {Roo.EventObject} e
42477          */
42478         "cellclick" : true,
42479         /**
42480          * @event celldblclick
42481          * Fires when a cell is double clicked
42482          * @param {Grid} this
42483          * @param {Number} rowIndex
42484          * @param {Number} columnIndex
42485          * @param {Roo.EventObject} e
42486          */
42487         "celldblclick" : true,
42488         /**
42489          * @event rowclick
42490          * Fires when a row is clicked
42491          * @param {Grid} this
42492          * @param {Number} rowIndex
42493          * @param {Roo.EventObject} e
42494          */
42495         "rowclick" : true,
42496         /**
42497          * @event rowdblclick
42498          * Fires when a row is double clicked
42499          * @param {Grid} this
42500          * @param {Number} rowIndex
42501          * @param {Roo.EventObject} e
42502          */
42503         "rowdblclick" : true,
42504         /**
42505          * @event headerclick
42506          * Fires when a header is clicked
42507          * @param {Grid} this
42508          * @param {Number} columnIndex
42509          * @param {Roo.EventObject} e
42510          */
42511         "headerclick" : true,
42512         /**
42513          * @event headerdblclick
42514          * Fires when a header cell is double clicked
42515          * @param {Grid} this
42516          * @param {Number} columnIndex
42517          * @param {Roo.EventObject} e
42518          */
42519         "headerdblclick" : true,
42520         /**
42521          * @event rowcontextmenu
42522          * Fires when a row is right clicked
42523          * @param {Grid} this
42524          * @param {Number} rowIndex
42525          * @param {Roo.EventObject} e
42526          */
42527         "rowcontextmenu" : true,
42528         /**
42529          * @event cellcontextmenu
42530          * Fires when a cell is right clicked
42531          * @param {Grid} this
42532          * @param {Number} rowIndex
42533          * @param {Number} cellIndex
42534          * @param {Roo.EventObject} e
42535          */
42536          "cellcontextmenu" : true,
42537         /**
42538          * @event headercontextmenu
42539          * Fires when a header is right clicked
42540          * @param {Grid} this
42541          * @param {Number} columnIndex
42542          * @param {Roo.EventObject} e
42543          */
42544         "headercontextmenu" : true,
42545         /**
42546          * @event bodyscroll
42547          * Fires when the body element is scrolled
42548          * @param {Number} scrollLeft
42549          * @param {Number} scrollTop
42550          */
42551         "bodyscroll" : true,
42552         /**
42553          * @event columnresize
42554          * Fires when the user resizes a column
42555          * @param {Number} columnIndex
42556          * @param {Number} newSize
42557          */
42558         "columnresize" : true,
42559         /**
42560          * @event columnmove
42561          * Fires when the user moves a column
42562          * @param {Number} oldIndex
42563          * @param {Number} newIndex
42564          */
42565         "columnmove" : true,
42566         /**
42567          * @event startdrag
42568          * Fires when row(s) start being dragged
42569          * @param {Grid} this
42570          * @param {Roo.GridDD} dd The drag drop object
42571          * @param {event} e The raw browser event
42572          */
42573         "startdrag" : true,
42574         /**
42575          * @event enddrag
42576          * Fires when a drag operation is complete
42577          * @param {Grid} this
42578          * @param {Roo.GridDD} dd The drag drop object
42579          * @param {event} e The raw browser event
42580          */
42581         "enddrag" : true,
42582         /**
42583          * @event dragdrop
42584          * Fires when dragged row(s) are dropped on a valid DD target
42585          * @param {Grid} this
42586          * @param {Roo.GridDD} dd The drag drop object
42587          * @param {String} targetId The target drag drop object
42588          * @param {event} e The raw browser event
42589          */
42590         "dragdrop" : true,
42591         /**
42592          * @event dragover
42593          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
42594          * @param {Grid} this
42595          * @param {Roo.GridDD} dd The drag drop object
42596          * @param {String} targetId The target drag drop object
42597          * @param {event} e The raw browser event
42598          */
42599         "dragover" : true,
42600         /**
42601          * @event dragenter
42602          *  Fires when the dragged row(s) first cross another DD target while being dragged
42603          * @param {Grid} this
42604          * @param {Roo.GridDD} dd The drag drop object
42605          * @param {String} targetId The target drag drop object
42606          * @param {event} e The raw browser event
42607          */
42608         "dragenter" : true,
42609         /**
42610          * @event dragout
42611          * Fires when the dragged row(s) leave another DD target while being dragged
42612          * @param {Grid} this
42613          * @param {Roo.GridDD} dd The drag drop object
42614          * @param {String} targetId The target drag drop object
42615          * @param {event} e The raw browser event
42616          */
42617         "dragout" : true,
42618         /**
42619          * @event rowclass
42620          * Fires when a row is rendered, so you can change add a style to it.
42621          * @param {GridView} gridview   The grid view
42622          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
42623          */
42624         'rowclass' : true,
42625
42626         /**
42627          * @event render
42628          * Fires when the grid is rendered
42629          * @param {Grid} grid
42630          */
42631         'render' : true,
42632             /**
42633              * @event select
42634              * Fires when a date is selected
42635              * @param {DatePicker} this
42636              * @param {Date} date The selected date
42637              */
42638         'select': true,
42639         /**
42640              * @event monthchange
42641              * Fires when the displayed month changes 
42642              * @param {DatePicker} this
42643              * @param {Date} date The selected month
42644              */
42645         'monthchange': true,
42646         /**
42647              * @event evententer
42648              * Fires when mouse over an event
42649              * @param {Calendar} this
42650              * @param {event} Event
42651              */
42652         'evententer': true,
42653         /**
42654              * @event eventleave
42655              * Fires when the mouse leaves an
42656              * @param {Calendar} this
42657              * @param {event}
42658              */
42659         'eventleave': true,
42660         /**
42661              * @event eventclick
42662              * Fires when the mouse click an
42663              * @param {Calendar} this
42664              * @param {event}
42665              */
42666         'eventclick': true,
42667         /**
42668              * @event eventrender
42669              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42670              * @param {Calendar} this
42671              * @param {data} data to be modified
42672              */
42673         'eventrender': true
42674         
42675     });
42676
42677     Roo.grid.Grid.superclass.constructor.call(this);
42678     this.on('render', function() {
42679         this.view.el.addClass('x-grid-cal'); 
42680         
42681         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42682
42683     },this);
42684     
42685     if (!Roo.grid.Calendar.style) {
42686         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42687             
42688             
42689             '.x-grid-cal .x-grid-col' :  {
42690                 height: 'auto !important',
42691                 'vertical-align': 'top'
42692             },
42693             '.x-grid-cal  .fc-event-hori' : {
42694                 height: '14px'
42695             }
42696              
42697             
42698         }, Roo.id());
42699     }
42700
42701     
42702     
42703 };
42704 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42705     /**
42706      * @cfg {Store} eventStore The store that loads events.
42707      */
42708     eventStore : 25,
42709
42710      
42711     activeDate : false,
42712     startDay : 0,
42713     autoWidth : true,
42714     monitorWindowResize : false,
42715
42716     
42717     resizeColumns : function() {
42718         var col = (this.view.el.getWidth() / 7) - 3;
42719         // loop through cols, and setWidth
42720         for(var i =0 ; i < 7 ; i++){
42721             this.cm.setColumnWidth(i, col);
42722         }
42723     },
42724      setDate :function(date) {
42725         
42726         Roo.log('setDate?');
42727         
42728         this.resizeColumns();
42729         var vd = this.activeDate;
42730         this.activeDate = date;
42731 //        if(vd && this.el){
42732 //            var t = date.getTime();
42733 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42734 //                Roo.log('using add remove');
42735 //                
42736 //                this.fireEvent('monthchange', this, date);
42737 //                
42738 //                this.cells.removeClass("fc-state-highlight");
42739 //                this.cells.each(function(c){
42740 //                   if(c.dateValue == t){
42741 //                       c.addClass("fc-state-highlight");
42742 //                       setTimeout(function(){
42743 //                            try{c.dom.firstChild.focus();}catch(e){}
42744 //                       }, 50);
42745 //                       return false;
42746 //                   }
42747 //                   return true;
42748 //                });
42749 //                return;
42750 //            }
42751 //        }
42752         
42753         var days = date.getDaysInMonth();
42754         
42755         var firstOfMonth = date.getFirstDateOfMonth();
42756         var startingPos = firstOfMonth.getDay()-this.startDay;
42757         
42758         if(startingPos < this.startDay){
42759             startingPos += 7;
42760         }
42761         
42762         var pm = date.add(Date.MONTH, -1);
42763         var prevStart = pm.getDaysInMonth()-startingPos;
42764 //        
42765         
42766         
42767         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42768         
42769         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42770         //this.cells.addClassOnOver('fc-state-hover');
42771         
42772         var cells = this.cells.elements;
42773         var textEls = this.textNodes;
42774         
42775         //Roo.each(cells, function(cell){
42776         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42777         //});
42778         
42779         days += startingPos;
42780
42781         // convert everything to numbers so it's fast
42782         var day = 86400000;
42783         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42784         //Roo.log(d);
42785         //Roo.log(pm);
42786         //Roo.log(prevStart);
42787         
42788         var today = new Date().clearTime().getTime();
42789         var sel = date.clearTime().getTime();
42790         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42791         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42792         var ddMatch = this.disabledDatesRE;
42793         var ddText = this.disabledDatesText;
42794         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42795         var ddaysText = this.disabledDaysText;
42796         var format = this.format;
42797         
42798         var setCellClass = function(cal, cell){
42799             
42800             //Roo.log('set Cell Class');
42801             cell.title = "";
42802             var t = d.getTime();
42803             
42804             //Roo.log(d);
42805             
42806             
42807             cell.dateValue = t;
42808             if(t == today){
42809                 cell.className += " fc-today";
42810                 cell.className += " fc-state-highlight";
42811                 cell.title = cal.todayText;
42812             }
42813             if(t == sel){
42814                 // disable highlight in other month..
42815                 cell.className += " fc-state-highlight";
42816                 
42817             }
42818             // disabling
42819             if(t < min) {
42820                 //cell.className = " fc-state-disabled";
42821                 cell.title = cal.minText;
42822                 return;
42823             }
42824             if(t > max) {
42825                 //cell.className = " fc-state-disabled";
42826                 cell.title = cal.maxText;
42827                 return;
42828             }
42829             if(ddays){
42830                 if(ddays.indexOf(d.getDay()) != -1){
42831                     // cell.title = ddaysText;
42832                    // cell.className = " fc-state-disabled";
42833                 }
42834             }
42835             if(ddMatch && format){
42836                 var fvalue = d.dateFormat(format);
42837                 if(ddMatch.test(fvalue)){
42838                     cell.title = ddText.replace("%0", fvalue);
42839                    cell.className = " fc-state-disabled";
42840                 }
42841             }
42842             
42843             if (!cell.initialClassName) {
42844                 cell.initialClassName = cell.dom.className;
42845             }
42846             
42847             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
42848         };
42849
42850         var i = 0;
42851         
42852         for(; i < startingPos; i++) {
42853             cells[i].dayName =  (++prevStart);
42854             Roo.log(textEls[i]);
42855             d.setDate(d.getDate()+1);
42856             
42857             //cells[i].className = "fc-past fc-other-month";
42858             setCellClass(this, cells[i]);
42859         }
42860         
42861         var intDay = 0;
42862         
42863         for(; i < days; i++){
42864             intDay = i - startingPos + 1;
42865             cells[i].dayName =  (intDay);
42866             d.setDate(d.getDate()+1);
42867             
42868             cells[i].className = ''; // "x-date-active";
42869             setCellClass(this, cells[i]);
42870         }
42871         var extraDays = 0;
42872         
42873         for(; i < 42; i++) {
42874             //textEls[i].innerHTML = (++extraDays);
42875             
42876             d.setDate(d.getDate()+1);
42877             cells[i].dayName = (++extraDays);
42878             cells[i].className = "fc-future fc-other-month";
42879             setCellClass(this, cells[i]);
42880         }
42881         
42882         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42883         
42884         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42885         
42886         // this will cause all the cells to mis
42887         var rows= [];
42888         var i =0;
42889         for (var r = 0;r < 6;r++) {
42890             for (var c =0;c < 7;c++) {
42891                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42892             }    
42893         }
42894         
42895         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42896         for(i=0;i<cells.length;i++) {
42897             
42898             this.cells.elements[i].dayName = cells[i].dayName ;
42899             this.cells.elements[i].className = cells[i].className;
42900             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42901             this.cells.elements[i].title = cells[i].title ;
42902             this.cells.elements[i].dateValue = cells[i].dateValue ;
42903         }
42904         
42905         
42906         
42907         
42908         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42909         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42910         
42911         ////if(totalRows != 6){
42912             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42913            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42914        // }
42915         
42916         this.fireEvent('monthchange', this, date);
42917         
42918         
42919     },
42920  /**
42921      * Returns the grid's SelectionModel.
42922      * @return {SelectionModel}
42923      */
42924     getSelectionModel : function(){
42925         if(!this.selModel){
42926             this.selModel = new Roo.grid.CellSelectionModel();
42927         }
42928         return this.selModel;
42929     },
42930
42931     load: function() {
42932         this.eventStore.load()
42933         
42934         
42935         
42936     },
42937     
42938     findCell : function(dt) {
42939         dt = dt.clearTime().getTime();
42940         var ret = false;
42941         this.cells.each(function(c){
42942             //Roo.log("check " +c.dateValue + '?=' + dt);
42943             if(c.dateValue == dt){
42944                 ret = c;
42945                 return false;
42946             }
42947             return true;
42948         });
42949         
42950         return ret;
42951     },
42952     
42953     findCells : function(rec) {
42954         var s = rec.data.start_dt.clone().clearTime().getTime();
42955        // Roo.log(s);
42956         var e= rec.data.end_dt.clone().clearTime().getTime();
42957        // Roo.log(e);
42958         var ret = [];
42959         this.cells.each(function(c){
42960              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42961             
42962             if(c.dateValue > e){
42963                 return ;
42964             }
42965             if(c.dateValue < s){
42966                 return ;
42967             }
42968             ret.push(c);
42969         });
42970         
42971         return ret;    
42972     },
42973     
42974     findBestRow: function(cells)
42975     {
42976         var ret = 0;
42977         
42978         for (var i =0 ; i < cells.length;i++) {
42979             ret  = Math.max(cells[i].rows || 0,ret);
42980         }
42981         return ret;
42982         
42983     },
42984     
42985     
42986     addItem : function(rec)
42987     {
42988         // look for vertical location slot in
42989         var cells = this.findCells(rec);
42990         
42991         rec.row = this.findBestRow(cells);
42992         
42993         // work out the location.
42994         
42995         var crow = false;
42996         var rows = [];
42997         for(var i =0; i < cells.length; i++) {
42998             if (!crow) {
42999                 crow = {
43000                     start : cells[i],
43001                     end :  cells[i]
43002                 };
43003                 continue;
43004             }
43005             if (crow.start.getY() == cells[i].getY()) {
43006                 // on same row.
43007                 crow.end = cells[i];
43008                 continue;
43009             }
43010             // different row.
43011             rows.push(crow);
43012             crow = {
43013                 start: cells[i],
43014                 end : cells[i]
43015             };
43016             
43017         }
43018         
43019         rows.push(crow);
43020         rec.els = [];
43021         rec.rows = rows;
43022         rec.cells = cells;
43023         for (var i = 0; i < cells.length;i++) {
43024             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
43025             
43026         }
43027         
43028         
43029     },
43030     
43031     clearEvents: function() {
43032         
43033         if (!this.eventStore.getCount()) {
43034             return;
43035         }
43036         // reset number of rows in cells.
43037         Roo.each(this.cells.elements, function(c){
43038             c.rows = 0;
43039         });
43040         
43041         this.eventStore.each(function(e) {
43042             this.clearEvent(e);
43043         },this);
43044         
43045     },
43046     
43047     clearEvent : function(ev)
43048     {
43049         if (ev.els) {
43050             Roo.each(ev.els, function(el) {
43051                 el.un('mouseenter' ,this.onEventEnter, this);
43052                 el.un('mouseleave' ,this.onEventLeave, this);
43053                 el.remove();
43054             },this);
43055             ev.els = [];
43056         }
43057     },
43058     
43059     
43060     renderEvent : function(ev,ctr) {
43061         if (!ctr) {
43062              ctr = this.view.el.select('.fc-event-container',true).first();
43063         }
43064         
43065          
43066         this.clearEvent(ev);
43067             //code
43068        
43069         
43070         
43071         ev.els = [];
43072         var cells = ev.cells;
43073         var rows = ev.rows;
43074         this.fireEvent('eventrender', this, ev);
43075         
43076         for(var i =0; i < rows.length; i++) {
43077             
43078             cls = '';
43079             if (i == 0) {
43080                 cls += ' fc-event-start';
43081             }
43082             if ((i+1) == rows.length) {
43083                 cls += ' fc-event-end';
43084             }
43085             
43086             //Roo.log(ev.data);
43087             // how many rows should it span..
43088             var cg = this.eventTmpl.append(ctr,Roo.apply({
43089                 fccls : cls
43090                 
43091             }, ev.data) , true);
43092             
43093             
43094             cg.on('mouseenter' ,this.onEventEnter, this, ev);
43095             cg.on('mouseleave' ,this.onEventLeave, this, ev);
43096             cg.on('click', this.onEventClick, this, ev);
43097             
43098             ev.els.push(cg);
43099             
43100             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
43101             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
43102             //Roo.log(cg);
43103              
43104             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
43105             cg.setWidth(ebox.right - sbox.x -2);
43106         }
43107     },
43108     
43109     renderEvents: function()
43110     {   
43111         // first make sure there is enough space..
43112         
43113         if (!this.eventTmpl) {
43114             this.eventTmpl = new Roo.Template(
43115                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
43116                     '<div class="fc-event-inner">' +
43117                         '<span class="fc-event-time">{time}</span>' +
43118                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
43119                     '</div>' +
43120                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
43121                 '</div>'
43122             );
43123                 
43124         }
43125                
43126         
43127         
43128         this.cells.each(function(c) {
43129             //Roo.log(c.select('.fc-day-content div',true).first());
43130             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
43131         });
43132         
43133         var ctr = this.view.el.select('.fc-event-container',true).first();
43134         
43135         var cls;
43136         this.eventStore.each(function(ev){
43137             
43138             this.renderEvent(ev);
43139              
43140              
43141         }, this);
43142         this.view.layout();
43143         
43144     },
43145     
43146     onEventEnter: function (e, el,event,d) {
43147         this.fireEvent('evententer', this, el, event);
43148     },
43149     
43150     onEventLeave: function (e, el,event,d) {
43151         this.fireEvent('eventleave', this, el, event);
43152     },
43153     
43154     onEventClick: function (e, el,event,d) {
43155         this.fireEvent('eventclick', this, el, event);
43156     },
43157     
43158     onMonthChange: function () {
43159         this.store.load();
43160     },
43161     
43162     onLoad: function () {
43163         
43164         //Roo.log('calendar onload');
43165 //         
43166         if(this.eventStore.getCount() > 0){
43167             
43168            
43169             
43170             this.eventStore.each(function(d){
43171                 
43172                 
43173                 // FIXME..
43174                 var add =   d.data;
43175                 if (typeof(add.end_dt) == 'undefined')  {
43176                     Roo.log("Missing End time in calendar data: ");
43177                     Roo.log(d);
43178                     return;
43179                 }
43180                 if (typeof(add.start_dt) == 'undefined')  {
43181                     Roo.log("Missing Start time in calendar data: ");
43182                     Roo.log(d);
43183                     return;
43184                 }
43185                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
43186                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
43187                 add.id = add.id || d.id;
43188                 add.title = add.title || '??';
43189                 
43190                 this.addItem(d);
43191                 
43192              
43193             },this);
43194         }
43195         
43196         this.renderEvents();
43197     }
43198     
43199
43200 });
43201 /*
43202  grid : {
43203                 xtype: 'Grid',
43204                 xns: Roo.grid,
43205                 listeners : {
43206                     render : function ()
43207                     {
43208                         _this.grid = this;
43209                         
43210                         if (!this.view.el.hasClass('course-timesheet')) {
43211                             this.view.el.addClass('course-timesheet');
43212                         }
43213                         if (this.tsStyle) {
43214                             this.ds.load({});
43215                             return; 
43216                         }
43217                         Roo.log('width');
43218                         Roo.log(_this.grid.view.el.getWidth());
43219                         
43220                         
43221                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
43222                             '.course-timesheet .x-grid-row' : {
43223                                 height: '80px'
43224                             },
43225                             '.x-grid-row td' : {
43226                                 'vertical-align' : 0
43227                             },
43228                             '.course-edit-link' : {
43229                                 'color' : 'blue',
43230                                 'text-overflow' : 'ellipsis',
43231                                 'overflow' : 'hidden',
43232                                 'white-space' : 'nowrap',
43233                                 'cursor' : 'pointer'
43234                             },
43235                             '.sub-link' : {
43236                                 'color' : 'green'
43237                             },
43238                             '.de-act-sup-link' : {
43239                                 'color' : 'purple',
43240                                 'text-decoration' : 'line-through'
43241                             },
43242                             '.de-act-link' : {
43243                                 'color' : 'red',
43244                                 'text-decoration' : 'line-through'
43245                             },
43246                             '.course-timesheet .course-highlight' : {
43247                                 'border-top-style': 'dashed !important',
43248                                 'border-bottom-bottom': 'dashed !important'
43249                             },
43250                             '.course-timesheet .course-item' : {
43251                                 'font-family'   : 'tahoma, arial, helvetica',
43252                                 'font-size'     : '11px',
43253                                 'overflow'      : 'hidden',
43254                                 'padding-left'  : '10px',
43255                                 'padding-right' : '10px',
43256                                 'padding-top' : '10px' 
43257                             }
43258                             
43259                         }, Roo.id());
43260                                 this.ds.load({});
43261                     }
43262                 },
43263                 autoWidth : true,
43264                 monitorWindowResize : false,
43265                 cellrenderer : function(v,x,r)
43266                 {
43267                     return v;
43268                 },
43269                 sm : {
43270                     xtype: 'CellSelectionModel',
43271                     xns: Roo.grid
43272                 },
43273                 dataSource : {
43274                     xtype: 'Store',
43275                     xns: Roo.data,
43276                     listeners : {
43277                         beforeload : function (_self, options)
43278                         {
43279                             options.params = options.params || {};
43280                             options.params._month = _this.monthField.getValue();
43281                             options.params.limit = 9999;
43282                             options.params['sort'] = 'when_dt';    
43283                             options.params['dir'] = 'ASC';    
43284                             this.proxy.loadResponse = this.loadResponse;
43285                             Roo.log("load?");
43286                             //this.addColumns();
43287                         },
43288                         load : function (_self, records, options)
43289                         {
43290                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
43291                                 // if you click on the translation.. you can edit it...
43292                                 var el = Roo.get(this);
43293                                 var id = el.dom.getAttribute('data-id');
43294                                 var d = el.dom.getAttribute('data-date');
43295                                 var t = el.dom.getAttribute('data-time');
43296                                 //var id = this.child('span').dom.textContent;
43297                                 
43298                                 //Roo.log(this);
43299                                 Pman.Dialog.CourseCalendar.show({
43300                                     id : id,
43301                                     when_d : d,
43302                                     when_t : t,
43303                                     productitem_active : id ? 1 : 0
43304                                 }, function() {
43305                                     _this.grid.ds.load({});
43306                                 });
43307                            
43308                            });
43309                            
43310                            _this.panel.fireEvent('resize', [ '', '' ]);
43311                         }
43312                     },
43313                     loadResponse : function(o, success, response){
43314                             // this is overridden on before load..
43315                             
43316                             Roo.log("our code?");       
43317                             //Roo.log(success);
43318                             //Roo.log(response)
43319                             delete this.activeRequest;
43320                             if(!success){
43321                                 this.fireEvent("loadexception", this, o, response);
43322                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
43323                                 return;
43324                             }
43325                             var result;
43326                             try {
43327                                 result = o.reader.read(response);
43328                             }catch(e){
43329                                 Roo.log("load exception?");
43330                                 this.fireEvent("loadexception", this, o, response, e);
43331                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
43332                                 return;
43333                             }
43334                             Roo.log("ready...");        
43335                             // loop through result.records;
43336                             // and set this.tdate[date] = [] << array of records..
43337                             _this.tdata  = {};
43338                             Roo.each(result.records, function(r){
43339                                 //Roo.log(r.data);
43340                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
43341                                     _this.tdata[r.data.when_dt.format('j')] = [];
43342                                 }
43343                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
43344                             });
43345                             
43346                             //Roo.log(_this.tdata);
43347                             
43348                             result.records = [];
43349                             result.totalRecords = 6;
43350                     
43351                             // let's generate some duumy records for the rows.
43352                             //var st = _this.dateField.getValue();
43353                             
43354                             // work out monday..
43355                             //st = st.add(Date.DAY, -1 * st.format('w'));
43356                             
43357                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43358                             
43359                             var firstOfMonth = date.getFirstDayOfMonth();
43360                             var days = date.getDaysInMonth();
43361                             var d = 1;
43362                             var firstAdded = false;
43363                             for (var i = 0; i < result.totalRecords ; i++) {
43364                                 //var d= st.add(Date.DAY, i);
43365                                 var row = {};
43366                                 var added = 0;
43367                                 for(var w = 0 ; w < 7 ; w++){
43368                                     if(!firstAdded && firstOfMonth != w){
43369                                         continue;
43370                                     }
43371                                     if(d > days){
43372                                         continue;
43373                                     }
43374                                     firstAdded = true;
43375                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
43376                                     row['weekday'+w] = String.format(
43377                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
43378                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
43379                                                     d,
43380                                                     date.format('Y-m-')+dd
43381                                                 );
43382                                     added++;
43383                                     if(typeof(_this.tdata[d]) != 'undefined'){
43384                                         Roo.each(_this.tdata[d], function(r){
43385                                             var is_sub = '';
43386                                             var deactive = '';
43387                                             var id = r.id;
43388                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
43389                                             if(r.parent_id*1>0){
43390                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
43391                                                 id = r.parent_id;
43392                                             }
43393                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
43394                                                 deactive = 'de-act-link';
43395                                             }
43396                                             
43397                                             row['weekday'+w] += String.format(
43398                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
43399                                                     id, //0
43400                                                     r.product_id_name, //1
43401                                                     r.when_dt.format('h:ia'), //2
43402                                                     is_sub, //3
43403                                                     deactive, //4
43404                                                     desc // 5
43405                                             );
43406                                         });
43407                                     }
43408                                     d++;
43409                                 }
43410                                 
43411                                 // only do this if something added..
43412                                 if(added > 0){ 
43413                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
43414                                 }
43415                                 
43416                                 
43417                                 // push it twice. (second one with an hour..
43418                                 
43419                             }
43420                             //Roo.log(result);
43421                             this.fireEvent("load", this, o, o.request.arg);
43422                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
43423                         },
43424                     sortInfo : {field: 'when_dt', direction : 'ASC' },
43425                     proxy : {
43426                         xtype: 'HttpProxy',
43427                         xns: Roo.data,
43428                         method : 'GET',
43429                         url : baseURL + '/Roo/Shop_course.php'
43430                     },
43431                     reader : {
43432                         xtype: 'JsonReader',
43433                         xns: Roo.data,
43434                         id : 'id',
43435                         fields : [
43436                             {
43437                                 'name': 'id',
43438                                 'type': 'int'
43439                             },
43440                             {
43441                                 'name': 'when_dt',
43442                                 'type': 'string'
43443                             },
43444                             {
43445                                 'name': 'end_dt',
43446                                 'type': 'string'
43447                             },
43448                             {
43449                                 'name': 'parent_id',
43450                                 'type': 'int'
43451                             },
43452                             {
43453                                 'name': 'product_id',
43454                                 'type': 'int'
43455                             },
43456                             {
43457                                 'name': 'productitem_id',
43458                                 'type': 'int'
43459                             },
43460                             {
43461                                 'name': 'guid',
43462                                 'type': 'int'
43463                             }
43464                         ]
43465                     }
43466                 },
43467                 toolbar : {
43468                     xtype: 'Toolbar',
43469                     xns: Roo,
43470                     items : [
43471                         {
43472                             xtype: 'Button',
43473                             xns: Roo.Toolbar,
43474                             listeners : {
43475                                 click : function (_self, e)
43476                                 {
43477                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43478                                     sd.setMonth(sd.getMonth()-1);
43479                                     _this.monthField.setValue(sd.format('Y-m-d'));
43480                                     _this.grid.ds.load({});
43481                                 }
43482                             },
43483                             text : "Back"
43484                         },
43485                         {
43486                             xtype: 'Separator',
43487                             xns: Roo.Toolbar
43488                         },
43489                         {
43490                             xtype: 'MonthField',
43491                             xns: Roo.form,
43492                             listeners : {
43493                                 render : function (_self)
43494                                 {
43495                                     _this.monthField = _self;
43496                                    // _this.monthField.set  today
43497                                 },
43498                                 select : function (combo, date)
43499                                 {
43500                                     _this.grid.ds.load({});
43501                                 }
43502                             },
43503                             value : (function() { return new Date(); })()
43504                         },
43505                         {
43506                             xtype: 'Separator',
43507                             xns: Roo.Toolbar
43508                         },
43509                         {
43510                             xtype: 'TextItem',
43511                             xns: Roo.Toolbar,
43512                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
43513                         },
43514                         {
43515                             xtype: 'Fill',
43516                             xns: Roo.Toolbar
43517                         },
43518                         {
43519                             xtype: 'Button',
43520                             xns: Roo.Toolbar,
43521                             listeners : {
43522                                 click : function (_self, e)
43523                                 {
43524                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43525                                     sd.setMonth(sd.getMonth()+1);
43526                                     _this.monthField.setValue(sd.format('Y-m-d'));
43527                                     _this.grid.ds.load({});
43528                                 }
43529                             },
43530                             text : "Next"
43531                         }
43532                     ]
43533                 },
43534                  
43535             }
43536         };
43537         
43538         *//*
43539  * Based on:
43540  * Ext JS Library 1.1.1
43541  * Copyright(c) 2006-2007, Ext JS, LLC.
43542  *
43543  * Originally Released Under LGPL - original licence link has changed is not relivant.
43544  *
43545  * Fork - LGPL
43546  * <script type="text/javascript">
43547  */
43548  
43549 /**
43550  * @class Roo.LoadMask
43551  * A simple utility class for generically masking elements while loading data.  If the element being masked has
43552  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
43553  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
43554  * element's UpdateManager load indicator and will be destroyed after the initial load.
43555  * @constructor
43556  * Create a new LoadMask
43557  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
43558  * @param {Object} config The config object
43559  */
43560 Roo.LoadMask = function(el, config){
43561     this.el = Roo.get(el);
43562     Roo.apply(this, config);
43563     if(this.store){
43564         this.store.on('beforeload', this.onBeforeLoad, this);
43565         this.store.on('load', this.onLoad, this);
43566         this.store.on('loadexception', this.onLoadException, this);
43567         this.removeMask = false;
43568     }else{
43569         var um = this.el.getUpdateManager();
43570         um.showLoadIndicator = false; // disable the default indicator
43571         um.on('beforeupdate', this.onBeforeLoad, this);
43572         um.on('update', this.onLoad, this);
43573         um.on('failure', this.onLoad, this);
43574         this.removeMask = true;
43575     }
43576 };
43577
43578 Roo.LoadMask.prototype = {
43579     /**
43580      * @cfg {Boolean} removeMask
43581      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
43582      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
43583      */
43584     removeMask : false,
43585     /**
43586      * @cfg {String} msg
43587      * The text to display in a centered loading message box (defaults to 'Loading...')
43588      */
43589     msg : 'Loading...',
43590     /**
43591      * @cfg {String} msgCls
43592      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
43593      */
43594     msgCls : 'x-mask-loading',
43595
43596     /**
43597      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
43598      * @type Boolean
43599      */
43600     disabled: false,
43601
43602     /**
43603      * Disables the mask to prevent it from being displayed
43604      */
43605     disable : function(){
43606        this.disabled = true;
43607     },
43608
43609     /**
43610      * Enables the mask so that it can be displayed
43611      */
43612     enable : function(){
43613         this.disabled = false;
43614     },
43615     
43616     onLoadException : function()
43617     {
43618         Roo.log(arguments);
43619         
43620         if (typeof(arguments[3]) != 'undefined') {
43621             Roo.MessageBox.alert("Error loading",arguments[3]);
43622         } 
43623         /*
43624         try {
43625             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43626                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43627             }   
43628         } catch(e) {
43629             
43630         }
43631         */
43632     
43633         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43634     },
43635     // private
43636     onLoad : function()
43637     {
43638         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43639     },
43640
43641     // private
43642     onBeforeLoad : function(){
43643         if(!this.disabled){
43644             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
43645         }
43646     },
43647
43648     // private
43649     destroy : function(){
43650         if(this.store){
43651             this.store.un('beforeload', this.onBeforeLoad, this);
43652             this.store.un('load', this.onLoad, this);
43653             this.store.un('loadexception', this.onLoadException, this);
43654         }else{
43655             var um = this.el.getUpdateManager();
43656             um.un('beforeupdate', this.onBeforeLoad, this);
43657             um.un('update', this.onLoad, this);
43658             um.un('failure', this.onLoad, this);
43659         }
43660     }
43661 };/*
43662  * Based on:
43663  * Ext JS Library 1.1.1
43664  * Copyright(c) 2006-2007, Ext JS, LLC.
43665  *
43666  * Originally Released Under LGPL - original licence link has changed is not relivant.
43667  *
43668  * Fork - LGPL
43669  * <script type="text/javascript">
43670  */
43671
43672
43673 /**
43674  * @class Roo.XTemplate
43675  * @extends Roo.Template
43676  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43677 <pre><code>
43678 var t = new Roo.XTemplate(
43679         '&lt;select name="{name}"&gt;',
43680                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
43681         '&lt;/select&gt;'
43682 );
43683  
43684 // then append, applying the master template values
43685  </code></pre>
43686  *
43687  * Supported features:
43688  *
43689  *  Tags:
43690
43691 <pre><code>
43692       {a_variable} - output encoded.
43693       {a_variable.format:("Y-m-d")} - call a method on the variable
43694       {a_variable:raw} - unencoded output
43695       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43696       {a_variable:this.method_on_template(...)} - call a method on the template object.
43697  
43698 </code></pre>
43699  *  The tpl tag:
43700 <pre><code>
43701         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
43702         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
43703         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
43704         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
43705   
43706         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
43707         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
43708 </code></pre>
43709  *      
43710  */
43711 Roo.XTemplate = function()
43712 {
43713     Roo.XTemplate.superclass.constructor.apply(this, arguments);
43714     if (this.html) {
43715         this.compile();
43716     }
43717 };
43718
43719
43720 Roo.extend(Roo.XTemplate, Roo.Template, {
43721
43722     /**
43723      * The various sub templates
43724      */
43725     tpls : false,
43726     /**
43727      *
43728      * basic tag replacing syntax
43729      * WORD:WORD()
43730      *
43731      * // you can fake an object call by doing this
43732      *  x.t:(test,tesT) 
43733      * 
43734      */
43735     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43736
43737     /**
43738      * compile the template
43739      *
43740      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43741      *
43742      */
43743     compile: function()
43744     {
43745         var s = this.html;
43746      
43747         s = ['<tpl>', s, '</tpl>'].join('');
43748     
43749         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43750             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43751             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
43752             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43753             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
43754             m,
43755             id     = 0,
43756             tpls   = [];
43757     
43758         while(true == !!(m = s.match(re))){
43759             var forMatch   = m[0].match(nameRe),
43760                 ifMatch   = m[0].match(ifRe),
43761                 execMatch   = m[0].match(execRe),
43762                 namedMatch   = m[0].match(namedRe),
43763                 
43764                 exp  = null, 
43765                 fn   = null,
43766                 exec = null,
43767                 name = forMatch && forMatch[1] ? forMatch[1] : '';
43768                 
43769             if (ifMatch) {
43770                 // if - puts fn into test..
43771                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43772                 if(exp){
43773                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43774                 }
43775             }
43776             
43777             if (execMatch) {
43778                 // exec - calls a function... returns empty if true is  returned.
43779                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43780                 if(exp){
43781                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43782                 }
43783             }
43784             
43785             
43786             if (name) {
43787                 // for = 
43788                 switch(name){
43789                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43790                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43791                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43792                 }
43793             }
43794             var uid = namedMatch ? namedMatch[1] : id;
43795             
43796             
43797             tpls.push({
43798                 id:     namedMatch ? namedMatch[1] : id,
43799                 target: name,
43800                 exec:   exec,
43801                 test:   fn,
43802                 body:   m[1] || ''
43803             });
43804             if (namedMatch) {
43805                 s = s.replace(m[0], '');
43806             } else { 
43807                 s = s.replace(m[0], '{xtpl'+ id + '}');
43808             }
43809             ++id;
43810         }
43811         this.tpls = [];
43812         for(var i = tpls.length-1; i >= 0; --i){
43813             this.compileTpl(tpls[i]);
43814             this.tpls[tpls[i].id] = tpls[i];
43815         }
43816         this.master = tpls[tpls.length-1];
43817         return this;
43818     },
43819     /**
43820      * same as applyTemplate, except it's done to one of the subTemplates
43821      * when using named templates, you can do:
43822      *
43823      * var str = pl.applySubTemplate('your-name', values);
43824      *
43825      * 
43826      * @param {Number} id of the template
43827      * @param {Object} values to apply to template
43828      * @param {Object} parent (normaly the instance of this object)
43829      */
43830     applySubTemplate : function(id, values, parent)
43831     {
43832         
43833         
43834         var t = this.tpls[id];
43835         
43836         
43837         try { 
43838             if(t.test && !t.test.call(this, values, parent)){
43839                 return '';
43840             }
43841         } catch(e) {
43842             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43843             Roo.log(e.toString());
43844             Roo.log(t.test);
43845             return ''
43846         }
43847         try { 
43848             
43849             if(t.exec && t.exec.call(this, values, parent)){
43850                 return '';
43851             }
43852         } catch(e) {
43853             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43854             Roo.log(e.toString());
43855             Roo.log(t.exec);
43856             return ''
43857         }
43858         try {
43859             var vs = t.target ? t.target.call(this, values, parent) : values;
43860             parent = t.target ? values : parent;
43861             if(t.target && vs instanceof Array){
43862                 var buf = [];
43863                 for(var i = 0, len = vs.length; i < len; i++){
43864                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
43865                 }
43866                 return buf.join('');
43867             }
43868             return t.compiled.call(this, vs, parent);
43869         } catch (e) {
43870             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43871             Roo.log(e.toString());
43872             Roo.log(t.compiled);
43873             return '';
43874         }
43875     },
43876
43877     compileTpl : function(tpl)
43878     {
43879         var fm = Roo.util.Format;
43880         var useF = this.disableFormats !== true;
43881         var sep = Roo.isGecko ? "+" : ",";
43882         var undef = function(str) {
43883             Roo.log("Property not found :"  + str);
43884             return '';
43885         };
43886         
43887         var fn = function(m, name, format, args)
43888         {
43889             //Roo.log(arguments);
43890             args = args ? args.replace(/\\'/g,"'") : args;
43891             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43892             if (typeof(format) == 'undefined') {
43893                 format= 'htmlEncode';
43894             }
43895             if (format == 'raw' ) {
43896                 format = false;
43897             }
43898             
43899             if(name.substr(0, 4) == 'xtpl'){
43900                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43901             }
43902             
43903             // build an array of options to determine if value is undefined..
43904             
43905             // basically get 'xxxx.yyyy' then do
43906             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43907             //    (function () { Roo.log("Property not found"); return ''; })() :
43908             //    ......
43909             
43910             var udef_ar = [];
43911             var lookfor = '';
43912             Roo.each(name.split('.'), function(st) {
43913                 lookfor += (lookfor.length ? '.': '') + st;
43914                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
43915             });
43916             
43917             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43918             
43919             
43920             if(format && useF){
43921                 
43922                 args = args ? ',' + args : "";
43923                  
43924                 if(format.substr(0, 5) != "this."){
43925                     format = "fm." + format + '(';
43926                 }else{
43927                     format = 'this.call("'+ format.substr(5) + '", ';
43928                     args = ", values";
43929                 }
43930                 
43931                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
43932             }
43933              
43934             if (args.length) {
43935                 // called with xxyx.yuu:(test,test)
43936                 // change to ()
43937                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
43938             }
43939             // raw.. - :raw modifier..
43940             return "'"+ sep + udef_st  + name + ")"+sep+"'";
43941             
43942         };
43943         var body;
43944         // branched to use + in gecko and [].join() in others
43945         if(Roo.isGecko){
43946             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
43947                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43948                     "';};};";
43949         }else{
43950             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
43951             body.push(tpl.body.replace(/(\r\n|\n)/g,
43952                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43953             body.push("'].join('');};};");
43954             body = body.join('');
43955         }
43956         
43957         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43958        
43959         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
43960         eval(body);
43961         
43962         return this;
43963     },
43964
43965     applyTemplate : function(values){
43966         return this.master.compiled.call(this, values, {});
43967         //var s = this.subs;
43968     },
43969
43970     apply : function(){
43971         return this.applyTemplate.apply(this, arguments);
43972     }
43973
43974  });
43975
43976 Roo.XTemplate.from = function(el){
43977     el = Roo.getDom(el);
43978     return new Roo.XTemplate(el.value || el.innerHTML);
43979 };Roo.dialog = {};
43980 /*
43981 * Licence: LGPL
43982 */
43983
43984 /**
43985  * @class Roo.dialog.UploadCropbox
43986  * @extends Roo.BoxComponent
43987  * Dialog UploadCropbox class
43988  * @cfg {String} emptyText show when image has been loaded
43989  * @cfg {String} rotateNotify show when image too small to rotate
43990  * @cfg {Number} errorTimeout default 3000
43991  * @cfg {Number} minWidth default 300
43992  * @cfg {Number} minHeight default 300
43993  * @cfg {Number} outputMaxWidth default 1200
43994  * @cfg {Number} windowSize default 300
43995  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
43996  * @cfg {Boolean} isDocument (true|false) default false
43997  * @cfg {String} url action url
43998  * @cfg {String} paramName default 'imageUpload'
43999  * @cfg {String} method default POST
44000  * @cfg {Boolean} loadMask (true|false) default true
44001  * @cfg {Boolean} loadingText default 'Loading...'
44002  * 
44003  * @constructor
44004  * Create a new UploadCropbox
44005  * @param {Object} config The config object
44006  */
44007
44008  Roo.dialog.UploadCropbox = function(config){
44009     Roo.dialog.UploadCropbox.superclass.constructor.call(this, config);
44010     
44011     this.addEvents({
44012         /**
44013          * @event beforeselectfile
44014          * Fire before select file
44015          * @param {Roo.dialog.UploadCropbox} this
44016          */
44017         "beforeselectfile" : true,
44018         /**
44019          * @event initial
44020          * Fire after initEvent
44021          * @param {Roo.dialog.UploadCropbox} this
44022          */
44023         "initial" : true,
44024         /**
44025          * @event crop
44026          * Fire after initEvent
44027          * @param {Roo.dialog.UploadCropbox} this
44028          * @param {String} data
44029          */
44030         "crop" : true,
44031         /**
44032          * @event prepare
44033          * Fire when preparing the file data
44034          * @param {Roo.dialog.UploadCropbox} this
44035          * @param {Object} file
44036          */
44037         "prepare" : true,
44038         /**
44039          * @event exception
44040          * Fire when get exception
44041          * @param {Roo.dialog.UploadCropbox} this
44042          * @param {XMLHttpRequest} xhr
44043          */
44044         "exception" : true,
44045         /**
44046          * @event beforeloadcanvas
44047          * Fire before load the canvas
44048          * @param {Roo.dialog.UploadCropbox} this
44049          * @param {String} src
44050          */
44051         "beforeloadcanvas" : true,
44052         /**
44053          * @event trash
44054          * Fire when trash image
44055          * @param {Roo.dialog.UploadCropbox} this
44056          */
44057         "trash" : true,
44058         /**
44059          * @event download
44060          * Fire when download the image
44061          * @param {Roo.dialog.UploadCropbox} this
44062          */
44063         "download" : true,
44064         /**
44065          * @event footerbuttonclick
44066          * Fire when footerbuttonclick
44067          * @param {Roo.dialog.UploadCropbox} this
44068          * @param {String} type
44069          */
44070         "footerbuttonclick" : true,
44071         /**
44072          * @event resize
44073          * Fire when resize
44074          * @param {Roo.dialog.UploadCropbox} this
44075          */
44076         "resize" : true,
44077         /**
44078          * @event rotate
44079          * Fire when rotate the image
44080          * @param {Roo.dialog.UploadCropbox} this
44081          * @param {String} pos
44082          */
44083         "rotate" : true,
44084         /**
44085          * @event inspect
44086          * Fire when inspect the file
44087          * @param {Roo.dialog.UploadCropbox} this
44088          * @param {Object} file
44089          */
44090         "inspect" : true,
44091         /**
44092          * @event upload
44093          * Fire when xhr upload the file
44094          * @param {Roo.dialog.UploadCropbox} this
44095          * @param {Object} data
44096          */
44097         "upload" : true,
44098         /**
44099          * @event arrange
44100          * Fire when arrange the file data
44101          * @param {Roo.dialog.UploadCropbox} this
44102          * @param {Object} formData
44103          */
44104         "arrange" : true,
44105         /**
44106          * @event loadcanvas
44107          * Fire after load the canvas
44108          * @param {Roo.dialog.UploadCropbox}
44109          * @param {Object} imgEl
44110          */
44111         "loadcanvas" : true
44112     });
44113     
44114     this.buttons = this.buttons || Roo.dialog.UploadCropbox.footer.STANDARD;
44115 };
44116
44117 Roo.extend(Roo.dialog.UploadCropbox, Roo.Component,  {
44118     
44119     emptyText : 'Click to upload image',
44120     rotateNotify : 'Image is too small to rotate',
44121     errorTimeout : 3000,
44122     scale : 0,
44123     baseScale : 1,
44124     rotate : 0,
44125     dragable : false,
44126     pinching : false,
44127     mouseX : 0,
44128     mouseY : 0,
44129     cropData : false,
44130     minWidth : 300,
44131     minHeight : 300,
44132     outputMaxWidth : 1200,
44133     windowSize : 300,
44134     file : false,
44135     exif : {},
44136     baseRotate : 1,
44137     cropType : 'image/jpeg',
44138     buttons : false,
44139     canvasLoaded : false,
44140     isDocument : false,
44141     method : 'POST',
44142     paramName : 'imageUpload',
44143     loadMask : true,
44144     loadingText : 'Loading...',
44145     maskEl : false,
44146     
44147     getAutoCreate : function()
44148     {
44149         var cfg = {
44150             tag : 'div',
44151             cls : 'roo-upload-cropbox',
44152             cn : [
44153                 {
44154                     tag : 'input',
44155                     cls : 'roo-upload-cropbox-selector',
44156                     type : 'file'
44157                 },
44158                 {
44159                     tag : 'div',
44160                     cls : 'roo-upload-cropbox-body',
44161                     style : 'cursor:pointer',
44162                     cn : [
44163                         {
44164                             tag : 'div',
44165                             cls : 'roo-upload-cropbox-preview'
44166                         },
44167                         {
44168                             tag : 'div',
44169                             cls : 'roo-upload-cropbox-thumb'
44170                         },
44171                         {
44172                             tag : 'div',
44173                             cls : 'roo-upload-cropbox-empty-notify',
44174                             html : this.emptyText
44175                         },
44176                         {
44177                             tag : 'div',
44178                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
44179                             html : this.rotateNotify
44180                         }
44181                     ]
44182                 },
44183                 {
44184                     tag : 'div',
44185                     cls : 'roo-upload-cropbox-footer',
44186                     cn : {
44187                         tag : 'div',
44188                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
44189                         cn : []
44190                     }
44191                 }
44192             ]
44193         };
44194         
44195         return cfg;
44196     },
44197     
44198     onRender : function(ct, position)
44199     {
44200         Roo.dialog.UploadCropbox.superclass.onRender.call(this, ct, position);
44201
44202         if(this.el){
44203             if (this.el.attr('xtype')) {
44204                 this.el.attr('xtypex', this.el.attr('xtype'));
44205                 this.el.dom.removeAttribute('xtype');
44206                 
44207                 this.initEvents();
44208             }
44209         }
44210         else {
44211             var cfg = Roo.apply({},  this.getAutoCreate());
44212         
44213             cfg.id = this.id || Roo.id();
44214             
44215             if (this.cls) {
44216                 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
44217             }
44218             
44219             if (this.style) { // fixme needs to support more complex style data.
44220                 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
44221             }
44222             
44223             this.el = ct.createChild(cfg, position);
44224             
44225             this.initEvents();
44226         }
44227         
44228         if (this.buttons.length) {
44229             
44230             Roo.each(this.buttons, function(bb) {
44231                 
44232                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
44233                 
44234                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
44235                 
44236             }, this);
44237         }
44238         
44239         if(this.loadMask){
44240             this.maskEl = this.el;
44241         }
44242     },
44243     
44244     initEvents : function()
44245     {
44246         this.urlAPI = (window.createObjectURL && window) || 
44247                                 (window.URL && URL.revokeObjectURL && URL) || 
44248                                 (window.webkitURL && webkitURL);
44249                         
44250         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
44251         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44252         
44253         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
44254         this.selectorEl.hide();
44255         
44256         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
44257         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44258         
44259         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
44260         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44261         this.thumbEl.hide();
44262         
44263         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
44264         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44265         
44266         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
44267         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44268         this.errorEl.hide();
44269         
44270         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
44271         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44272         this.footerEl.hide();
44273         
44274         this.setThumbBoxSize();
44275         
44276         this.bind();
44277         
44278         this.resize();
44279         
44280         this.fireEvent('initial', this);
44281     },
44282
44283     bind : function()
44284     {
44285         var _this = this;
44286         
44287         window.addEventListener("resize", function() { _this.resize(); } );
44288         
44289         this.bodyEl.on('click', this.beforeSelectFile, this);
44290         
44291         if(Roo.isTouch){
44292             this.bodyEl.on('touchstart', this.onTouchStart, this);
44293             this.bodyEl.on('touchmove', this.onTouchMove, this);
44294             this.bodyEl.on('touchend', this.onTouchEnd, this);
44295         }
44296         
44297         if(!Roo.isTouch){
44298             this.bodyEl.on('mousedown', this.onMouseDown, this);
44299             this.bodyEl.on('mousemove', this.onMouseMove, this);
44300             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
44301             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
44302             Roo.get(document).on('mouseup', this.onMouseUp, this);
44303         }
44304         
44305         this.selectorEl.on('change', this.onFileSelected, this);
44306     },
44307     
44308     reset : function()
44309     {    
44310         this.scale = 0;
44311         this.baseScale = 1;
44312         this.rotate = 0;
44313         this.baseRotate = 1;
44314         this.dragable = false;
44315         this.pinching = false;
44316         this.mouseX = 0;
44317         this.mouseY = 0;
44318         this.cropData = false;
44319         this.notifyEl.dom.innerHTML = this.emptyText;
44320         
44321         // this.selectorEl.dom.value = '';
44322         
44323     },
44324     
44325     resize : function()
44326     {
44327         if(this.fireEvent('resize', this) != false){
44328             this.setThumbBoxPosition();
44329             this.setCanvasPosition();
44330         }
44331     },
44332     
44333     onFooterButtonClick : function(e, el, o, type)
44334     {
44335         switch (type) {
44336             case 'rotate-left' :
44337                 this.onRotateLeft(e);
44338                 break;
44339             case 'rotate-right' :
44340                 this.onRotateRight(e);
44341                 break;
44342             case 'picture' :
44343                 this.beforeSelectFile(e);
44344                 break;
44345             case 'trash' :
44346                 this.trash(e);
44347                 break;
44348             case 'crop' :
44349                 this.crop(e);
44350                 break;
44351             case 'download' :
44352                 this.download(e);
44353                 break;
44354             case 'center' :
44355                 this.center(e);
44356                 break;
44357             default :
44358                 break;
44359         }
44360         
44361         this.fireEvent('footerbuttonclick', this, type);
44362     },
44363     
44364     beforeSelectFile : function(e)
44365     {
44366         e.preventDefault();
44367         
44368         if(this.fireEvent('beforeselectfile', this) != false){
44369             this.selectorEl.dom.click();
44370         }
44371     },
44372     
44373     onFileSelected : function(e)
44374     {
44375         e.preventDefault();
44376         
44377         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
44378             return;
44379         }
44380         
44381         var file = this.selectorEl.dom.files[0];
44382         
44383         if(this.fireEvent('inspect', this, file) != false){
44384             this.prepare(file);
44385         }
44386         
44387     },
44388     
44389     trash : function(e)
44390     {
44391         this.fireEvent('trash', this);
44392     },
44393     
44394     download : function(e)
44395     {
44396         this.fireEvent('download', this);
44397     },
44398
44399     center : function(e)
44400     {
44401         this.setCanvasPosition();
44402     },
44403     
44404     loadCanvas : function(src)
44405     {   
44406         if(this.fireEvent('beforeloadcanvas', this, src) != false){
44407             
44408             this.reset();
44409             
44410             this.imageEl = document.createElement('img');
44411             
44412             var _this = this;
44413             
44414             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
44415             
44416             this.imageEl.src = src;
44417         }
44418     },
44419     
44420     onLoadCanvas : function()
44421     {   
44422         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
44423         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
44424
44425         if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
44426         
44427             this.bodyEl.un('click', this.beforeSelectFile, this);
44428             
44429             this.notifyEl.hide();
44430             this.thumbEl.show();
44431             this.footerEl.show();
44432             
44433             this.baseRotateLevel();
44434             
44435             if(this.isDocument){
44436                 this.setThumbBoxSize();
44437             }
44438             
44439             this.setThumbBoxPosition();
44440             
44441             this.baseScaleLevel();
44442             
44443             this.draw();
44444             
44445             this.resize();
44446             
44447             this.canvasLoaded = true;
44448         
44449         }
44450         
44451         if(this.loadMask){
44452             this.maskEl.unmask();
44453         }
44454         
44455     },
44456     
44457     setCanvasPosition : function(center = true)
44458     {   
44459         if(!this.canvasEl){
44460             return;
44461         }
44462
44463         var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
44464         var newCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
44465
44466         if(center) {
44467             this.previewEl.setLeft(newCenterLeft);
44468             this.previewEl.setTop(newCenterTop);
44469
44470             return;
44471         }
44472         
44473         var oldScaleLevel = this.baseScale * Math.pow(1.02, this.startScale);
44474         var oldCanvasWidth = Math.floor(this.imageEl.OriginWidth * oldScaleLevel);
44475         var oldCanvasHeight = Math.floor(this.imageEl.OriginHeight * oldScaleLevel);
44476
44477         var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - oldCanvasWidth) / 2);
44478         var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - oldCanvasHeight) / 2);
44479
44480         var leftDiff = newCenterLeft - oldCenterLeft;
44481         var topDiff = newCenterTop - oldCenterTop;
44482
44483         var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
44484         var newPreviewTop = this.previewEl.getTop(true) + topDiff;
44485
44486         this.previewEl.setLeft(newPreviewLeft);
44487         this.previewEl.setTop(newPreviewTop);
44488         
44489     },
44490     
44491     onMouseDown : function(e)
44492     {   
44493         e.stopEvent();
44494         
44495         this.dragable = true;
44496         this.pinching = false;
44497         
44498         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
44499             this.dragable = false;
44500             return;
44501         }
44502         
44503         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
44504         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
44505         
44506     },
44507     
44508     onMouseMove : function(e)
44509     {   
44510         e.stopEvent();
44511         
44512         if(!this.canvasLoaded){
44513             return;
44514         }
44515         
44516         if (!this.dragable){
44517             return;
44518         }
44519
44520         var maxPaddingLeft = this.canvasEl.width / 0.9 * 0.05;
44521         var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
44522
44523         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
44524             maxPaddingLeft = (this.canvasEl.height * this.minWidth / this.minHeight - this.canvasEl.width) / 2 + maxPaddingLeft;
44525         }
44526
44527         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
44528             maxPaddingTop = (this.canvasEl.width * this.minHeight / this.minWidth - this.canvasEl.height) / 2 + maxPaddingTop;
44529         }
44530         
44531         var minX = Math.ceil(this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - this.canvasEl.width - maxPaddingLeft);
44532         var minY = Math.ceil(this.thumbEl.getTop(true) + this.thumbEl.getHeight() - this.canvasEl.height - maxPaddingTop);
44533         
44534         var maxX = Math.ceil(this.thumbEl.getLeft(true) + maxPaddingLeft);
44535         var maxY = Math.ceil(this.thumbEl.getTop(true) +  maxPaddingTop);
44536
44537         if(minX > maxX) {
44538             var tempX = minX;
44539             minX = maxX;
44540             maxX = tempX;
44541         }
44542
44543         if(minY > maxY) {
44544             var tempY = minY;
44545             minY = maxY;
44546             maxY = tempY;
44547         }
44548
44549         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
44550         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
44551         
44552         x = x - this.mouseX;
44553         y = y - this.mouseY;
44554
44555         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
44556         var bgY = Math.ceil(y + this.previewEl.getTop(true));
44557         
44558         bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
44559         bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
44560         
44561         this.previewEl.setLeft(bgX);
44562         this.previewEl.setTop(bgY);
44563         
44564         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
44565         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
44566     },
44567     
44568     onMouseUp : function(e)
44569     {   
44570         e.stopEvent();
44571         
44572         this.dragable = false;
44573     },
44574     
44575     onMouseWheel : function(e)
44576     {   
44577         e.stopEvent();
44578         
44579         this.startScale = this.scale;
44580         this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
44581         
44582         if(!this.zoomable()){
44583             this.scale = this.startScale;
44584             return;
44585         }
44586
44587         
44588         this.draw();
44589         
44590         return;
44591     },
44592     
44593     zoomable : function()
44594     {
44595         var minScale = this.thumbEl.getWidth() / this.minWidth;
44596         
44597         if(this.minWidth < this.minHeight){
44598             minScale = this.thumbEl.getHeight() / this.minHeight;
44599         }
44600         
44601         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
44602         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
44603  
44604         var maxWidth = this.imageEl.OriginWidth;
44605         var maxHeight = this.imageEl.OriginHeight;
44606
44607
44608         var newCanvasWidth = Math.floor(this.imageEl.OriginWidth * this.getScaleLevel());
44609         var newCanvasHeight = Math.floor(this.imageEl.OriginHeight * this.getScaleLevel());
44610
44611         var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
44612         var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
44613
44614         var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - newCanvasWidth) / 2);
44615         var newCenterTop = Math.ceil((this.bodyEl.getHeight() - newCanvasHeight) / 2);
44616
44617         var leftDiff = newCenterLeft - oldCenterLeft;
44618         var topDiff = newCenterTop - oldCenterTop;
44619
44620         var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
44621         var newPreviewTop = this.previewEl.getTop(true) + topDiff;
44622
44623         var paddingLeft = newPreviewLeft - this.thumbEl.getLeft(true);
44624         var paddingTop = newPreviewTop - this.thumbEl.getTop(true);
44625
44626         var paddingRight = this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - newCanvasWidth - newPreviewLeft;
44627         var paddingBottom = this.thumbEl.getTop(true) + this.thumbEl.getHeight() - newCanvasHeight - newPreviewTop;
44628
44629         var maxPaddingLeft = newCanvasWidth / 0.9 * 0.05;
44630         var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
44631
44632         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
44633             maxPaddingLeft = (newCanvasHeight * this.minWidth / this.minHeight - newCanvasWidth) / 2 + maxPaddingLeft;
44634         }
44635
44636         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
44637             maxPaddingTop = (newCanvasWidth * this.minHeight / this.minWidth - newCanvasHeight) / 2 + maxPaddingTop;
44638         }
44639         
44640         if(
44641                 this.isDocument &&
44642                 (this.rotate == 0 || this.rotate == 180) && 
44643                 (
44644                     width > this.imageEl.OriginWidth || 
44645                     height > this.imageEl.OriginHeight ||
44646                     (width < this.minWidth && height < this.minHeight)
44647                 )
44648         ){
44649             return false;
44650         }
44651         
44652         if(
44653                 this.isDocument &&
44654                 (this.rotate == 90 || this.rotate == 270) && 
44655                 (
44656                     width > this.imageEl.OriginWidth || 
44657                     height > this.imageEl.OriginHeight ||
44658                     (width < this.minHeight && height < this.minWidth)
44659                 )
44660         ){
44661             return false;
44662         }
44663         
44664         if(
44665                 !this.isDocument &&
44666                 (this.rotate == 0 || this.rotate == 180) && 
44667                 (
44668                     // for zoom out
44669                     paddingLeft > maxPaddingLeft ||
44670                     paddingRight > maxPaddingLeft ||
44671                     paddingTop > maxPaddingTop ||
44672                     paddingBottom > maxPaddingTop ||
44673                     // for zoom in
44674                     width > maxWidth ||
44675                     height > maxHeight
44676                 )
44677         ){
44678             return false;
44679         }
44680         
44681         if(
44682                 !this.isDocument &&
44683                 (this.rotate == 90 || this.rotate == 270) && 
44684                 (
44685                     width < this.minHeight || 
44686                     width > this.imageEl.OriginWidth || 
44687                     height < this.minWidth || 
44688                     height > this.imageEl.OriginHeight
44689                 )
44690         ){
44691             return false;
44692         }
44693         
44694         return true;
44695         
44696     },
44697     
44698     onRotateLeft : function(e)
44699     {   
44700         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
44701             
44702             var minScale = this.thumbEl.getWidth() / this.minWidth;
44703             
44704             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
44705             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
44706             
44707             this.startScale = this.scale;
44708             
44709             while (this.getScaleLevel() < minScale){
44710             
44711                 this.scale = this.scale + 1;
44712                 
44713                 if(!this.zoomable()){
44714                     break;
44715                 }
44716                 
44717                 if(
44718                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
44719                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
44720                 ){
44721                     continue;
44722                 }
44723                 
44724                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
44725
44726                 this.draw();
44727                 
44728                 return;
44729             }
44730             
44731             this.scale = this.startScale;
44732             
44733             this.onRotateFail();
44734             
44735             return false;
44736         }
44737         
44738         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
44739
44740         if(this.isDocument){
44741             this.setThumbBoxSize();
44742             this.setThumbBoxPosition();
44743             this.setCanvasPosition();
44744         }
44745         
44746         this.draw();
44747         
44748         this.fireEvent('rotate', this, 'left');
44749         
44750     },
44751     
44752     onRotateRight : function(e)
44753     {
44754         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
44755             
44756             var minScale = this.thumbEl.getWidth() / this.minWidth;
44757         
44758             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
44759             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
44760             
44761             this.startScale = this.scale;
44762             
44763             while (this.getScaleLevel() < minScale){
44764             
44765                 this.scale = this.scale + 1;
44766                 
44767                 if(!this.zoomable()){
44768                     break;
44769                 }
44770                 
44771                 if(
44772                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
44773                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
44774                 ){
44775                     continue;
44776                 }
44777                 
44778                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
44779
44780                 this.draw();
44781                 
44782                 return;
44783             }
44784             
44785             this.scale = this.startScale;
44786             
44787             this.onRotateFail();
44788             
44789             return false;
44790         }
44791         
44792         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
44793
44794         if(this.isDocument){
44795             this.setThumbBoxSize();
44796             this.setThumbBoxPosition();
44797             this.setCanvasPosition();
44798         }
44799         
44800         this.draw();
44801         
44802         this.fireEvent('rotate', this, 'right');
44803     },
44804     
44805     onRotateFail : function()
44806     {
44807         this.errorEl.show(true);
44808         
44809         var _this = this;
44810         
44811         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
44812     },
44813     
44814     draw : function()
44815     {
44816         this.previewEl.dom.innerHTML = '';
44817         
44818         var canvasEl = document.createElement("canvas");
44819         
44820         var contextEl = canvasEl.getContext("2d");
44821         
44822         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
44823         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
44824         var center = this.imageEl.OriginWidth / 2;
44825         
44826         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
44827             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
44828             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
44829             center = this.imageEl.OriginHeight / 2;
44830         }
44831         
44832         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
44833         
44834         contextEl.translate(center, center);
44835         contextEl.rotate(this.rotate * Math.PI / 180);
44836
44837         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
44838         
44839         this.canvasEl = document.createElement("canvas");
44840         
44841         this.contextEl = this.canvasEl.getContext("2d");
44842         
44843         switch (this.rotate) {
44844             case 0 :
44845                 
44846                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
44847                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
44848                 
44849                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
44850                 
44851                 break;
44852             case 90 : 
44853                 
44854                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
44855                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
44856                 
44857                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
44858                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
44859                     break;
44860                 }
44861                 
44862                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
44863                 
44864                 break;
44865             case 180 :
44866                 
44867                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
44868                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
44869                 
44870                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
44871                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
44872                     break;
44873                 }
44874                 
44875                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
44876                 
44877                 break;
44878             case 270 :
44879                 
44880                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
44881                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
44882         
44883                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
44884                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
44885                     break;
44886                 }
44887                 
44888                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
44889                 
44890                 break;
44891             default : 
44892                 break;
44893         }
44894         
44895         this.previewEl.appendChild(this.canvasEl);
44896         
44897         this.setCanvasPosition(false);
44898     },
44899     
44900     crop : function()
44901     {
44902         if(!this.canvasLoaded){
44903             return;
44904         }
44905         
44906         var imageCanvas = document.createElement("canvas");
44907         
44908         var imageContext = imageCanvas.getContext("2d");
44909         
44910         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
44911         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
44912         
44913         var center = imageCanvas.width / 2;
44914         
44915         imageContext.translate(center, center);
44916         
44917         imageContext.rotate(this.rotate * Math.PI / 180);
44918         
44919         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
44920         
44921         var canvas = document.createElement("canvas");
44922         
44923         var context = canvas.getContext("2d");
44924
44925         canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
44926         
44927         canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
44928
44929         switch (this.rotate) {
44930             case 0 :
44931                 
44932                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
44933                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
44934                 
44935                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
44936                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
44937                 
44938                 var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
44939                 var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
44940
44941                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
44942                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
44943
44944                 if(canvas.width > this.outputMaxWidth) {
44945                     var scale = this.outputMaxWidth / canvas.width;
44946                     canvas.width = canvas.width * scale;
44947                     canvas.height = canvas.height * scale;
44948                     context.scale(scale, scale);
44949                 }
44950
44951                 context.fillStyle = 'white';
44952                 context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
44953
44954
44955                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
44956                 
44957                 break;
44958             case 90 : 
44959                 
44960                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
44961                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
44962                 
44963                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
44964                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
44965                 
44966                 var targetWidth = this.minWidth - 2 * x;
44967                 var targetHeight = this.minHeight - 2 * y;
44968                 
44969                 var scale = 1;
44970                 
44971                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
44972                     scale = targetWidth / width;
44973                 }
44974                 
44975                 if(x > 0 && y == 0){
44976                     scale = targetHeight / height;
44977                 }
44978                 
44979                 if(x > 0 && y > 0){
44980                     scale = targetWidth / width;
44981                     
44982                     if(width < height){
44983                         scale = targetHeight / height;
44984                     }
44985                 }
44986                 
44987                 context.scale(scale, scale);
44988                 
44989                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
44990                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
44991
44992                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
44993                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
44994                 
44995                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
44996                 
44997                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
44998                 
44999                 break;
45000             case 180 :
45001                 
45002                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
45003                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
45004                 
45005                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
45006                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
45007                 
45008                 var targetWidth = this.minWidth - 2 * x;
45009                 var targetHeight = this.minHeight - 2 * y;
45010                 
45011                 var scale = 1;
45012                 
45013                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
45014                     scale = targetWidth / width;
45015                 }
45016                 
45017                 if(x > 0 && y == 0){
45018                     scale = targetHeight / height;
45019                 }
45020                 
45021                 if(x > 0 && y > 0){
45022                     scale = targetWidth / width;
45023                     
45024                     if(width < height){
45025                         scale = targetHeight / height;
45026                     }
45027                 }
45028                 
45029                 context.scale(scale, scale);
45030                 
45031                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
45032                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
45033
45034                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
45035                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
45036
45037                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
45038                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
45039                 
45040                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
45041                 
45042                 break;
45043             case 270 :
45044                 
45045                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
45046                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
45047                 
45048                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
45049                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
45050                 
45051                 var targetWidth = this.minWidth - 2 * x;
45052                 var targetHeight = this.minHeight - 2 * y;
45053                 
45054                 var scale = 1;
45055                 
45056                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
45057                     scale = targetWidth / width;
45058                 }
45059                 
45060                 if(x > 0 && y == 0){
45061                     scale = targetHeight / height;
45062                 }
45063                 
45064                 if(x > 0 && y > 0){
45065                     scale = targetWidth / width;
45066                     
45067                     if(width < height){
45068                         scale = targetHeight / height;
45069                     }
45070                 }
45071                 
45072                 context.scale(scale, scale);
45073                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
45074                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
45075
45076                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
45077                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
45078                 
45079                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
45080                 
45081                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
45082                 
45083                 break;
45084             default : 
45085                 break;
45086         }
45087         
45088         this.cropData = canvas.toDataURL(this.cropType);
45089         
45090         if(this.fireEvent('crop', this, this.cropData) !== false){
45091             this.process(this.file, this.cropData);
45092         }
45093         
45094         return;
45095         
45096     },
45097     
45098     setThumbBoxSize : function()
45099     {
45100         var width, height;
45101         
45102         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
45103             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
45104             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
45105             
45106             this.minWidth = width;
45107             this.minHeight = height;
45108             
45109             if(this.rotate == 90 || this.rotate == 270){
45110                 this.minWidth = height;
45111                 this.minHeight = width;
45112             }
45113         }
45114         
45115         height = this.windowSize;
45116         width = Math.ceil(this.minWidth * height / this.minHeight);
45117         
45118         if(this.minWidth > this.minHeight){
45119             width = this.windowSize;
45120             height = Math.ceil(this.minHeight * width / this.minWidth);
45121         }
45122         
45123         this.thumbEl.setStyle({
45124             width : width + 'px',
45125             height : height + 'px'
45126         });
45127
45128         return;
45129             
45130     },
45131     
45132     setThumbBoxPosition : function()
45133     {
45134         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
45135         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
45136         
45137         this.thumbEl.setLeft(x);
45138         this.thumbEl.setTop(y);
45139         
45140     },
45141     
45142     baseRotateLevel : function()
45143     {
45144         this.baseRotate = 1;
45145         
45146         if(
45147                 typeof(this.exif) != 'undefined' &&
45148                 typeof(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
45149                 [1, 3, 6, 8].indexOf(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != -1
45150         ){
45151             this.baseRotate = this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']];
45152         }
45153         
45154         this.rotate = Roo.dialog.UploadCropbox['Orientation'][this.baseRotate];
45155         
45156     },
45157     
45158     baseScaleLevel : function()
45159     {
45160         var width, height;
45161         
45162         if(this.isDocument){
45163             
45164             if(this.baseRotate == 6 || this.baseRotate == 8){
45165             
45166                 height = this.thumbEl.getHeight();
45167                 this.baseScale = height / this.imageEl.OriginWidth;
45168
45169                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
45170                     width = this.thumbEl.getWidth();
45171                     this.baseScale = width / this.imageEl.OriginHeight;
45172                 }
45173
45174                 return;
45175             }
45176
45177             height = this.thumbEl.getHeight();
45178             this.baseScale = height / this.imageEl.OriginHeight;
45179
45180             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
45181                 width = this.thumbEl.getWidth();
45182                 this.baseScale = width / this.imageEl.OriginWidth;
45183             }
45184
45185             return;
45186         }
45187         
45188         if(this.baseRotate == 6 || this.baseRotate == 8){
45189             
45190             width = this.thumbEl.getHeight();
45191             this.baseScale = width / this.imageEl.OriginHeight;
45192             
45193             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
45194                 height = this.thumbEl.getWidth();
45195                 this.baseScale = height / this.imageEl.OriginHeight;
45196             }
45197             
45198             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
45199                 height = this.thumbEl.getWidth();
45200                 this.baseScale = height / this.imageEl.OriginHeight;
45201                 
45202                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
45203                     width = this.thumbEl.getHeight();
45204                     this.baseScale = width / this.imageEl.OriginWidth;
45205                 }
45206             }
45207             
45208             return;
45209         }
45210         
45211         width = this.thumbEl.getWidth();
45212         this.baseScale = width / this.imageEl.OriginWidth;
45213         
45214         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
45215             height = this.thumbEl.getHeight();
45216             this.baseScale = height / this.imageEl.OriginHeight;
45217         }
45218         
45219         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
45220             
45221             height = this.thumbEl.getHeight();
45222             this.baseScale = height / this.imageEl.OriginHeight;
45223             
45224             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
45225                 width = this.thumbEl.getWidth();
45226                 this.baseScale = width / this.imageEl.OriginWidth;
45227             }
45228             
45229         }
45230
45231         if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
45232             this.baseScale = width / this.minWidth;
45233         }
45234
45235         return;
45236     },
45237     
45238     getScaleLevel : function()
45239     {
45240         return this.baseScale * Math.pow(1.02, this.scale);
45241     },
45242     
45243     onTouchStart : function(e)
45244     {
45245         if(!this.canvasLoaded){
45246             this.beforeSelectFile(e);
45247             return;
45248         }
45249         
45250         var touches = e.browserEvent.touches;
45251         
45252         if(!touches){
45253             return;
45254         }
45255         
45256         if(touches.length == 1){
45257             this.onMouseDown(e);
45258             return;
45259         }
45260         
45261         if(touches.length != 2){
45262             return;
45263         }
45264         
45265         var coords = [];
45266         
45267         for(var i = 0, finger; finger = touches[i]; i++){
45268             coords.push(finger.pageX, finger.pageY);
45269         }
45270         
45271         var x = Math.pow(coords[0] - coords[2], 2);
45272         var y = Math.pow(coords[1] - coords[3], 2);
45273         
45274         this.startDistance = Math.sqrt(x + y);
45275         
45276         this.startScale = this.scale;
45277         
45278         this.pinching = true;
45279         this.dragable = false;
45280         
45281     },
45282     
45283     onTouchMove : function(e)
45284     {
45285         if(!this.pinching && !this.dragable){
45286             return;
45287         }
45288         
45289         var touches = e.browserEvent.touches;
45290         
45291         if(!touches){
45292             return;
45293         }
45294         
45295         if(this.dragable){
45296             this.onMouseMove(e);
45297             return;
45298         }
45299         
45300         var coords = [];
45301         
45302         for(var i = 0, finger; finger = touches[i]; i++){
45303             coords.push(finger.pageX, finger.pageY);
45304         }
45305         
45306         var x = Math.pow(coords[0] - coords[2], 2);
45307         var y = Math.pow(coords[1] - coords[3], 2);
45308         
45309         this.endDistance = Math.sqrt(x + y);
45310         
45311         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
45312         
45313         if(!this.zoomable()){
45314             this.scale = this.startScale;
45315             return;
45316         }
45317         
45318         this.draw();
45319         
45320     },
45321     
45322     onTouchEnd : function(e)
45323     {
45324         this.pinching = false;
45325         this.dragable = false;
45326         
45327     },
45328     
45329     process : function(file, crop)
45330     {
45331         if(this.loadMask){
45332             this.maskEl.mask(this.loadingText);
45333         }
45334         
45335         this.xhr = new XMLHttpRequest();
45336         
45337         file.xhr = this.xhr;
45338
45339         this.xhr.open(this.method, this.url, true);
45340         
45341         var headers = {
45342             "Accept": "application/json",
45343             "Cache-Control": "no-cache",
45344             "X-Requested-With": "XMLHttpRequest"
45345         };
45346         
45347         for (var headerName in headers) {
45348             var headerValue = headers[headerName];
45349             if (headerValue) {
45350                 this.xhr.setRequestHeader(headerName, headerValue);
45351             }
45352         }
45353         
45354         var _this = this;
45355         
45356         this.xhr.onload = function()
45357         {
45358             _this.xhrOnLoad(_this.xhr);
45359         }
45360         
45361         this.xhr.onerror = function()
45362         {
45363             _this.xhrOnError(_this.xhr);
45364         }
45365         
45366         var formData = new FormData();
45367
45368         formData.append('returnHTML', 'NO');
45369
45370         if(crop){
45371             formData.append('crop', crop);
45372             var blobBin = atob(crop.split(',')[1]);
45373             var array = [];
45374             for(var i = 0; i < blobBin.length; i++) {
45375                 array.push(blobBin.charCodeAt(i));
45376             }
45377             var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
45378             formData.append(this.paramName, croppedFile, file.name);
45379         }
45380         
45381         if(typeof(file.filename) != 'undefined'){
45382             formData.append('filename', file.filename);
45383         }
45384         
45385         if(typeof(file.mimetype) != 'undefined'){
45386             formData.append('mimetype', file.mimetype);
45387         }
45388
45389         if(this.fireEvent('arrange', this, formData) != false){
45390             this.xhr.send(formData);
45391         };
45392     },
45393     
45394     xhrOnLoad : function(xhr)
45395     {
45396         if(this.loadMask){
45397             this.maskEl.unmask();
45398         }
45399         
45400         if (xhr.readyState !== 4) {
45401             this.fireEvent('exception', this, xhr);
45402             return;
45403         }
45404
45405         var response = Roo.decode(xhr.responseText);
45406         
45407         if(!response.success){
45408             this.fireEvent('exception', this, xhr);
45409             return;
45410         }
45411         
45412         var response = Roo.decode(xhr.responseText);
45413         
45414         this.fireEvent('upload', this, response);
45415         
45416     },
45417     
45418     xhrOnError : function()
45419     {
45420         if(this.loadMask){
45421             this.maskEl.unmask();
45422         }
45423         
45424         Roo.log('xhr on error');
45425         
45426         var response = Roo.decode(xhr.responseText);
45427           
45428         Roo.log(response);
45429         
45430     },
45431     
45432     prepare : function(file)
45433     {   
45434         if(this.loadMask){
45435             this.maskEl.mask(this.loadingText);
45436         }
45437         
45438         this.file = false;
45439         this.exif = {};
45440         
45441         if(typeof(file) === 'string'){
45442             this.loadCanvas(file);
45443             return;
45444         }
45445         
45446         if(!file || !this.urlAPI){
45447             return;
45448         }
45449         
45450         this.file = file;
45451         if(typeof(file.type) != 'undefined' && file.type.length != 0) {
45452             this.cropType = file.type;
45453         }
45454         
45455         var _this = this;
45456         
45457         if(this.fireEvent('prepare', this, this.file) != false){
45458             
45459             var reader = new FileReader();
45460             
45461             reader.onload = function (e) {
45462                 if (e.target.error) {
45463                     Roo.log(e.target.error);
45464                     return;
45465                 }
45466                 
45467                 var buffer = e.target.result,
45468                     dataView = new DataView(buffer),
45469                     offset = 2,
45470                     maxOffset = dataView.byteLength - 4,
45471                     markerBytes,
45472                     markerLength;
45473                 
45474                 if (dataView.getUint16(0) === 0xffd8) {
45475                     while (offset < maxOffset) {
45476                         markerBytes = dataView.getUint16(offset);
45477                         
45478                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
45479                             markerLength = dataView.getUint16(offset + 2) + 2;
45480                             if (offset + markerLength > dataView.byteLength) {
45481                                 Roo.log('Invalid meta data: Invalid segment size.');
45482                                 break;
45483                             }
45484                             
45485                             if(markerBytes == 0xffe1){
45486                                 _this.parseExifData(
45487                                     dataView,
45488                                     offset,
45489                                     markerLength
45490                                 );
45491                             }
45492                             
45493                             offset += markerLength;
45494                             
45495                             continue;
45496                         }
45497                         
45498                         break;
45499                     }
45500                     
45501                 }
45502                 
45503                 var url = _this.urlAPI.createObjectURL(_this.file);
45504                 
45505                 _this.loadCanvas(url);
45506                 
45507                 return;
45508             }
45509             
45510             reader.readAsArrayBuffer(this.file);
45511             
45512         }
45513         
45514     },
45515     
45516     parseExifData : function(dataView, offset, length)
45517     {
45518         var tiffOffset = offset + 10,
45519             littleEndian,
45520             dirOffset;
45521     
45522         if (dataView.getUint32(offset + 4) !== 0x45786966) {
45523             // No Exif data, might be XMP data instead
45524             return;
45525         }
45526         
45527         // Check for the ASCII code for "Exif" (0x45786966):
45528         if (dataView.getUint32(offset + 4) !== 0x45786966) {
45529             // No Exif data, might be XMP data instead
45530             return;
45531         }
45532         if (tiffOffset + 8 > dataView.byteLength) {
45533             Roo.log('Invalid Exif data: Invalid segment size.');
45534             return;
45535         }
45536         // Check for the two null bytes:
45537         if (dataView.getUint16(offset + 8) !== 0x0000) {
45538             Roo.log('Invalid Exif data: Missing byte alignment offset.');
45539             return;
45540         }
45541         // Check the byte alignment:
45542         switch (dataView.getUint16(tiffOffset)) {
45543         case 0x4949:
45544             littleEndian = true;
45545             break;
45546         case 0x4D4D:
45547             littleEndian = false;
45548             break;
45549         default:
45550             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
45551             return;
45552         }
45553         // Check for the TIFF tag marker (0x002A):
45554         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
45555             Roo.log('Invalid Exif data: Missing TIFF marker.');
45556             return;
45557         }
45558         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
45559         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
45560         
45561         this.parseExifTags(
45562             dataView,
45563             tiffOffset,
45564             tiffOffset + dirOffset,
45565             littleEndian
45566         );
45567     },
45568     
45569     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
45570     {
45571         var tagsNumber,
45572             dirEndOffset,
45573             i;
45574         if (dirOffset + 6 > dataView.byteLength) {
45575             Roo.log('Invalid Exif data: Invalid directory offset.');
45576             return;
45577         }
45578         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
45579         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
45580         if (dirEndOffset + 4 > dataView.byteLength) {
45581             Roo.log('Invalid Exif data: Invalid directory size.');
45582             return;
45583         }
45584         for (i = 0; i < tagsNumber; i += 1) {
45585             this.parseExifTag(
45586                 dataView,
45587                 tiffOffset,
45588                 dirOffset + 2 + 12 * i, // tag offset
45589                 littleEndian
45590             );
45591         }
45592         // Return the offset to the next directory:
45593         return dataView.getUint32(dirEndOffset, littleEndian);
45594     },
45595     
45596     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
45597     {
45598         var tag = dataView.getUint16(offset, littleEndian);
45599         
45600         this.exif[tag] = this.getExifValue(
45601             dataView,
45602             tiffOffset,
45603             offset,
45604             dataView.getUint16(offset + 2, littleEndian), // tag type
45605             dataView.getUint32(offset + 4, littleEndian), // tag length
45606             littleEndian
45607         );
45608     },
45609     
45610     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
45611     {
45612         var tagType = Roo.dialog.UploadCropbox.exifTagTypes[type],
45613             tagSize,
45614             dataOffset,
45615             values,
45616             i,
45617             str,
45618             c;
45619     
45620         if (!tagType) {
45621             Roo.log('Invalid Exif data: Invalid tag type.');
45622             return;
45623         }
45624         
45625         tagSize = tagType.size * length;
45626         // Determine if the value is contained in the dataOffset bytes,
45627         // or if the value at the dataOffset is a pointer to the actual data:
45628         dataOffset = tagSize > 4 ?
45629                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
45630         if (dataOffset + tagSize > dataView.byteLength) {
45631             Roo.log('Invalid Exif data: Invalid data offset.');
45632             return;
45633         }
45634         if (length === 1) {
45635             return tagType.getValue(dataView, dataOffset, littleEndian);
45636         }
45637         values = [];
45638         for (i = 0; i < length; i += 1) {
45639             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
45640         }
45641         
45642         if (tagType.ascii) {
45643             str = '';
45644             // Concatenate the chars:
45645             for (i = 0; i < values.length; i += 1) {
45646                 c = values[i];
45647                 // Ignore the terminating NULL byte(s):
45648                 if (c === '\u0000') {
45649                     break;
45650                 }
45651                 str += c;
45652             }
45653             return str;
45654         }
45655         return values;
45656     }
45657     
45658 });
45659
45660 Roo.apply(Roo.dialog.UploadCropbox, {
45661     tags : {
45662         'Orientation': 0x0112
45663     },
45664     
45665     Orientation: {
45666             1: 0, //'top-left',
45667 //            2: 'top-right',
45668             3: 180, //'bottom-right',
45669 //            4: 'bottom-left',
45670 //            5: 'left-top',
45671             6: 90, //'right-top',
45672 //            7: 'right-bottom',
45673             8: 270 //'left-bottom'
45674     },
45675     
45676     exifTagTypes : {
45677         // byte, 8-bit unsigned int:
45678         1: {
45679             getValue: function (dataView, dataOffset) {
45680                 return dataView.getUint8(dataOffset);
45681             },
45682             size: 1
45683         },
45684         // ascii, 8-bit byte:
45685         2: {
45686             getValue: function (dataView, dataOffset) {
45687                 return String.fromCharCode(dataView.getUint8(dataOffset));
45688             },
45689             size: 1,
45690             ascii: true
45691         },
45692         // short, 16 bit int:
45693         3: {
45694             getValue: function (dataView, dataOffset, littleEndian) {
45695                 return dataView.getUint16(dataOffset, littleEndian);
45696             },
45697             size: 2
45698         },
45699         // long, 32 bit int:
45700         4: {
45701             getValue: function (dataView, dataOffset, littleEndian) {
45702                 return dataView.getUint32(dataOffset, littleEndian);
45703             },
45704             size: 4
45705         },
45706         // rational = two long values, first is numerator, second is denominator:
45707         5: {
45708             getValue: function (dataView, dataOffset, littleEndian) {
45709                 return dataView.getUint32(dataOffset, littleEndian) /
45710                     dataView.getUint32(dataOffset + 4, littleEndian);
45711             },
45712             size: 8
45713         },
45714         // slong, 32 bit signed int:
45715         9: {
45716             getValue: function (dataView, dataOffset, littleEndian) {
45717                 return dataView.getInt32(dataOffset, littleEndian);
45718             },
45719             size: 4
45720         },
45721         // srational, two slongs, first is numerator, second is denominator:
45722         10: {
45723             getValue: function (dataView, dataOffset, littleEndian) {
45724                 return dataView.getInt32(dataOffset, littleEndian) /
45725                     dataView.getInt32(dataOffset + 4, littleEndian);
45726             },
45727             size: 8
45728         }
45729     },
45730     
45731     footer : {
45732         STANDARD : [
45733             {
45734                 tag : 'div',
45735                 cls : 'btn-group roo-upload-cropbox-rotate-left',
45736                 action : 'rotate-left',
45737                 cn : [
45738                     {
45739                         tag : 'button',
45740                         cls : 'btn btn-default',
45741                         html : '<i class="fa fa-undo"></i>'
45742                     }
45743                 ]
45744             },
45745             {
45746                 tag : 'div',
45747                 cls : 'btn-group roo-upload-cropbox-picture',
45748                 action : 'picture',
45749                 cn : [
45750                     {
45751                         tag : 'button',
45752                         cls : 'btn btn-default',
45753                         html : '<i class="fa fa-picture-o"></i>'
45754                     }
45755                 ]
45756             },
45757             {
45758                 tag : 'div',
45759                 cls : 'btn-group roo-upload-cropbox-rotate-right',
45760                 action : 'rotate-right',
45761                 cn : [
45762                     {
45763                         tag : 'button',
45764                         cls : 'btn btn-default',
45765                         html : '<i class="fa fa-repeat"></i>'
45766                     }
45767                 ]
45768             }
45769         ],
45770         DOCUMENT : [
45771             {
45772                 tag : 'div',
45773                 cls : 'btn-group roo-upload-cropbox-rotate-left',
45774                 action : 'rotate-left',
45775                 cn : [
45776                     {
45777                         tag : 'button',
45778                         cls : 'btn btn-default',
45779                         html : '<i class="fa fa-undo"></i>'
45780                     }
45781                 ]
45782             },
45783             {
45784                 tag : 'div',
45785                 cls : 'btn-group roo-upload-cropbox-download',
45786                 action : 'download',
45787                 cn : [
45788                     {
45789                         tag : 'button',
45790                         cls : 'btn btn-default',
45791                         html : '<i class="fa fa-download"></i>'
45792                     }
45793                 ]
45794             },
45795             {
45796                 tag : 'div',
45797                 cls : 'btn-group roo-upload-cropbox-crop',
45798                 action : 'crop',
45799                 cn : [
45800                     {
45801                         tag : 'button',
45802                         cls : 'btn btn-default',
45803                         html : '<i class="fa fa-crop"></i>'
45804                     }
45805                 ]
45806             },
45807             {
45808                 tag : 'div',
45809                 cls : 'btn-group roo-upload-cropbox-trash',
45810                 action : 'trash',
45811                 cn : [
45812                     {
45813                         tag : 'button',
45814                         cls : 'btn btn-default',
45815                         html : '<i class="fa fa-trash"></i>'
45816                     }
45817                 ]
45818             },
45819             {
45820                 tag : 'div',
45821                 cls : 'btn-group roo-upload-cropbox-rotate-right',
45822                 action : 'rotate-right',
45823                 cn : [
45824                     {
45825                         tag : 'button',
45826                         cls : 'btn btn-default',
45827                         html : '<i class="fa fa-repeat"></i>'
45828                     }
45829                 ]
45830             }
45831         ],
45832         ROTATOR : [
45833             {
45834                 tag : 'div',
45835                 cls : 'btn-group roo-upload-cropbox-rotate-left',
45836                 action : 'rotate-left',
45837                 cn : [
45838                     {
45839                         tag : 'button',
45840                         cls : 'btn btn-default',
45841                         html : '<i class="fa fa-undo"></i>'
45842                     }
45843                 ]
45844             },
45845             {
45846                 tag : 'div',
45847                 cls : 'btn-group roo-upload-cropbox-rotate-right',
45848                 action : 'rotate-right',
45849                 cn : [
45850                     {
45851                         tag : 'button',
45852                         cls : 'btn btn-default',
45853                         html : '<i class="fa fa-repeat"></i>'
45854                     }
45855                 ]
45856             }
45857         ],
45858         CENTER : [
45859             {
45860                 tag : 'div',
45861                 cls : 'btn-group roo-upload-cropbox-center',
45862                 action : 'center',
45863                 cn : [
45864                     {
45865                         tag : 'button',
45866                         cls : 'btn btn-default',
45867                         html : 'CENTER'
45868                     }
45869                 ]
45870             }
45871         ]
45872     }
45873 });